view release on metacpan or search on metacpan
bin/diff_spreadsheets view on Meta::CPAN
3334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
sheetname_from_spec filepath_from_spec form_spec_with_sheetname/
;
use
Term::ReadKey ();
require
PerlIO;
#$SIG{__WARN__} = sub{ Carp::cluck @_ };
sub
main::Differ::compile_if_regex(@);
#forward
# Replace invalid/undesirable filename characters with underscore
sub
sanitize_filename(_) {
local
$_
=
shift
; s/[^-._[:word:]]/_/g; s/_$//;
$_
}
use
utf8;
my
(
$visible_space
,
$RArrow
);
# By default set output encoding to match the user's terminal/locale,
# and suppress the "...does not mapt to..." warnings if unsupported characters
# are displayed e.g. in spreadsheet data diff displays (\x{hex} escapes will
# be displayed for un-encodeable characters).
# May be overridden by the --output-encoding option!
$PerlIO::encoding::fallback
|= Encode::ONLY_PRAGMA_WARNINGS;
no
warnings
'utf8'
;
use
open
':std'
,
':locale'
;
select
STDERR; $| = 1;
select
STDOUT; $| = 1;
my
$stdout_encoding
;
sub
decode_foruser($) {
my
$octets
=
shift
;
$stdout_encoding
? decode(
$stdout_encoding
,
$octets
, Encode::FB_DEFAULT|Encode::LEAVE_SRC)
:
$octets
}
sub
encode_foruser($) {
my
$chars
=
shift
;
$stdout_encoding
? encode(
$stdout_encoding
,
$chars
, Encode::FB_DEFAULT|Encode::LEAVE_SRC)
:
$chars
}
#-------- Get Arguments ---------------
sub
call_pod2usage {
confess
"bug"
if
(
scalar
(
@_
) % 2) != 0;
my
%opts
=
@_
;
bin/diff_spreadsheets view on Meta::CPAN
87888990919293949596979899100101102103104105106107108109110111112113114115116117118119# if (my $podpath = pod_where({-inc => 1},"App::diff_spreadsheets")) {
# $opts{-input} = $podpath;
# } else {
# warn "Could not find App::diff_spreadsheets in \@INC\n",
# "\@INC:\n", join("\n ",@INC), "\n";
# }
# }
pod2usage(\
%opts
);
}
sub
badargs_exit(@) {
call_pod2usage(
-output
=> \
*STDERR
,
-exitval
=> 2,
@_
);
}
# We could use Term::Encoding to detect the terminal's encoding
# but that would create a possibly-undesirable dependency.
# We just assume UTF-8, which these days is probably correct.
#my $rightarrow = '->';
#my $rightarrow = "\N{RIGHTWARDS ARROW}\N{THIN SPACE}";
#my $rightarrow = "\N{RIGHTWARDS ARROW}\N{NARROW NO-BREAK SPACE}";
#my $rightarrow = "\N{RIGHTWARDS ARROW}\N{HAIR SPACE}";
sub
_get_terminal_width() {
# returns undef if unknowable
if
(u(
$ENV
{COLUMNS}) =~ /^[1-9]\d*$/) {
return
$ENV
{COLUMNS};
# overrides actual terminal width
}
else
{
local
*_
;
# Try to avoid clobbering special filehandle "_"
# This does not actualy work; https://github.com/Perl/perl5/issues/19142
my
$fh
=
-t STDOUT ?
*STDOUT
:
-t STDERR ?
*STDERR
:
# under Windows the filehandle must be an *output* handle
bin/diff_spreadsheets view on Meta::CPAN
147148149150151152153154155156157158159160161162163164165166167168169170171my
%opts
= (
sort_rows
=> 1,
quote_char
=>
'"'
,
sep_char
=>
','
,
encoding
=>
'UTF-8'
,
trunc_title_width
=> max(20,
int
(
$maxwidth
/3)),
);
my
$method
=
"native"
;
my
$help
;
sub
dashopt($) {
my
$optname
=
shift
;
length
(
$optname
) == 1 ?
"-$optname"
:
"--$optname"
;
}
sub
combined_optval($@) {
# value is optional
my
(
$nilval
,
$optname
,
$val
) =
@_
;
if
(
$val
eq
$nilval
) {
return
dashopt(
$optname
);
}
else
{
# e.g. -w20 or --width=20
return
(
length
(
$optname
) == 1 ?
"-$optname${val}"
:
"--$optname=$val"
);
}
}
my
@diff_opts
;
bin/diff_spreadsheets view on Meta::CPAN
501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594########################################
#
#
#
########################################
package
main::Differ;
use
Carp;
use
Path::Tiny 0.144;
#use File::Temp qw(tempfile tempdir);
#use File::Basename qw(basename dirname fileparse);
#use File::Path qw(make_path remove_tree);
#use File::Spec::Functions qw(canonpath catfile catdir rootdir tmpdir);
use
Data::Dumper::Interp 6.007;
sub
_visualize($);
sub
_title_or_origABC($$);
my
$seen
= {};
sub
warnonce(@) {
my
$msg
=
join
""
,
@_
;
return
if
$seen
->{
$msg
}++;
warn
$msg
;
}
sub
cx2origcx($$) {
my
(
$sheet
,
$currcx
) =
@_
;
# 0 1 2 3 [4] [5] 6 7 [8] 9 10 original
# 0 1 2 3 4 5 6 7 after deletes
my
$deleted_cxs
=
$sheet
->attributes->{DELETED_CXS} // confess(
"bug"
);
my
$nskipped
= 0;
my
$oldcx
=
$currcx
;
for
my
$dcx
(
@$deleted_cxs
) {
return
(
$currcx
+
$nskipped
)
if
$dcx
>=
$oldcx
;
$oldcx
=
$dcx
;
++
$nskipped
;
}
return
(
$currcx
+
$nskipped
);
}
sub
cx2origlet($$) { cx2let(
&cx2origcx
) }
sub
compile_if_regex(@) {
# compile "/.../msix" strings to qr/.../msix
my
@specs
=
@_
;
foreach
(
@specs
) {
if
(m
#^(/.*/[a-z]*)\z|^m([/\[\{\(\<].*)\z#s) {
my
$regex
=
eval
"qr${1}"
//
do
{
$@ =~ s/ at \(
eval
.*//mg;
die
"$@ in $_\n"
;
};
$_
=
$regex
}
}
wantarray
?
@specs
:
@specs
> 1 ? confess(
"multiple results"
) :
$specs
[0]
}
sub
__truncate($$) {
my
(
$aref
,
$maxwid
) =
@_
;
my
$changed
;
foreach
(
@$aref
) {
if
(
length
(
$_
) >
$maxwid
) {
$changed
++;
# cut before the first actual newline or after visualized newline
s/\A.+?(?:\\n|(?=\n))\K.*/.../s;
if
(
length
(
$_
) >
$maxwid
) {
my
$numdots
= max(3,
length
(
$_
)-
$maxwid
);
substr
(
$_
,
$maxwid
-
$numdots
) = (
"."
x
$numdots
);
}
#warn dvis '##TT __truncated $_ (len=',length($_),")\n";
}
}
$changed
}
sub
__visualize($) {
local
$_
=
shift
;
s/\n/\\n/sg;
s/\t/\\t/g;
s/([^[:
:]])/
sprintf
"\\x{%02x}"
,
ord
($1) /eg;
if
(
$opts
{ign_leading_spaces}) {
s/^( +)//;
}
else
{
# make leading spaces visible
s/^( +)/
$visible_space
x
length
($1)/e;
}
bin/diff_spreadsheets view on Meta::CPAN
791792793794795796797798799800801802803804805806807808809810811812813814815816
}
# Append empty columns if needed to make both sheets the same width
for
my
$N
(1,2) {
my
$sh
=
$hash
{
"sheet$N"
};
while
(
$sh
->num_cols <
$ncols
) {
$sh
->insert_col(
'>$'
,
""
) }
}
bless
\
%hash
,
$class
;
}
sub
_getncols($$) {
my
(
$sh
,
$rx
) =
@_
;
$rx
>= (
$sh
->title_rx//0) ?
$sh
->attributes->{NUMCOLS_USED} :
$sh
->num_cols
}
sub
_title_or_origABC($$) {
my
(
$sh
,
$cx
) =
@_
;
my
$titlerow
=
$sh
->title_row;
(
defined
(
$titlerow
) &&
$titlerow
->[
$cx
] ne
""
)
?
$titlerow
->[
$cx
]
: cx2origlet(
$sh
,
$cx
)
}
sub
_native_output {
my
(
$self
,
$cxlist1
,
$cxlist2
,
$diff
,
$dumbrun
) =
@_
; oops
unless
@_
==5;
my
$restricted_keycols
= @{
$self
->{id_columns}} > 0;
bin/diff_spreadsheets view on Meta::CPAN
122512261227122812291230123112321233123412351236123712381239124012411242124312441245
$self
->{exit_status} = (
$changed
? 1 : 0);
}
# Format a value for display as an indented block.
# Newlines in the input are already converted to visible "\n".
# Actual newlines are appended to these markers and indentation
# inserted before second and subsequent lines. Quotes are not
# included in the result.
# Usage:
# printf "%*s: '%s'\n", $twid, $title, fmt_value($valstr,$twid+3);
sub
fmt_value($$) {
my
(
$str
,
$indent_width
) =
@_
;
oops
if
$str
=~ /\n/s;
my
$indent
=
" "
x
$indent_width
;
$str
=~ s/\\n/\\n\n${indent}/gs;
if
(
$maxwidth
) {
# fold
my
$first_mw
=
$maxwidth
-
$indent_width
;
if
(
length
(
$str
) >
$first_mw
) {
oops
"maxwidth $maxwidth is too narrow\nfmw=$first_mw iw=$indent_width <<$str>>"
if
$first_mw
< 20;
# sanity
$str
=~ s/\A([^\n]{
$first_mw
})([^\n]+)/$1\n${indent}$2/m;
bin/diff_spreadsheets view on Meta::CPAN
133313341335133613371338133913401341134213431344134513461347134813491350135113521353
}
next
if
$d
==0;
# both lists are the same
foreach
(
$diff
->Items(2)) {
$pfx
, ((
$d
&2) ?
"+ "
:
" "
), fmt_value(
$_
,
$twidth
+3),
"\n"
;
$pfx
=
$indent_str
;
}
}
}
}
}
#show_cell
sub
row_header($$;$) {
my
(
$verb
,
$rx1
,
$rx2
) =
@_
;
my
$s
=
"$verb row "
.(
$rx1
+1);
$s
.=
" (row "
.(
$rx2
+1).
" in 2nd file)"
if
defined
(
$rx2
) &&
$rx1
!=
$rx2
;
sprintf
"\n-------- %s %s\n"
,
$s
, (
'-'
x (
$maxwidth
-10-
length
(
$s
)));
}
sub
show_row {
#my ($self, $N, $rx, $verb, $dumbrun) = @_; oops if @_ != 5;
my
(
$self
,
$N
,
$row
,
$curr_rx
,
$verb
,
$dumbrun
) =
@_
; oops
if
@_
!= 6;
my
$sh
=
$self
->{
"sheet$N"
};
my
$orig_rx
=
$sh
->attributes->{ORIGINAL_RXS}->[
$curr_rx
] //
$curr_rx
;
t/t_Common.pm view on Meta::CPAN
5678910111213141516171819202122232425# Attribution is requested but is not required.
#
# PLEASE NOTE that the above applies to THIS FILE ONLY. Other files in the
# same distribution or in other collections may have more restrictive terms.
# Common setup stuff, not specifically for test cases.
# This file is intended to be identical in all my module distributions.
package
t_Common;
sub
hash2str($) {
my
$h
=
shift
;
join
(
""
,
map
{
" ${_}=>"
.(
$h
->{
$_
}//
"u"
)}
sort
keys
%$h
) }
my
(
$default_warnbits
,
$default_pragmas
);
BEGIN {
$default_warnbits
= ${^WARNING_BITS}//
"u"
;
$default_pragmas
= ($^H//
"u"
).
":"
.hash2str(\%^H);
}
use
strict;
# Do not fatalize decode warnings (under category 'utf8') because various smokers
# can have restrictive stdout encodings.
t/t_Common.pm view on Meta::CPAN
4041424344454647484950515253545556575859606162636465666768697071727374757677require
Exporter;
our
@EXPORT
=
qw/mytempfile mytempdir/
;
our
@EXPORT_OK
=
qw/oops btw btwN/
;
use
Import::Into;
use
Carp;
sub
oops(@) {
my
$pkg
=
caller
;
my
$pfx
=
"\noops"
;
$pfx
.=
" in pkg '$pkg'"
unless
$pkg
eq
'main'
;
$pfx
.=
":\n"
;
if
(
defined
(
&Spreadsheet::Edit::logmsg
)) {
# Show current apply sheet & row if any.
@_
=(
$pfx
,
&Spreadsheet::Edit::logmsg
(
@_
));
}
else
{
@_
=(
$pfx
,
@_
);
}
push
@_
,
"\n"
unless
$_
[-1] =~ /\R\z/;
goto
&Carp::confess
}
# "By The Way" messages showing file:linenum of the call
sub
btw(@) {
unshift
@_
,0;
goto
&btwN
}
sub
btwN($@) {
my
$N
=
shift
;
my
(
$fn
,
$lno
) = (
caller
(
$N
))[1,2];
$fn
=~ s/.*[\\\/]//;
$fn
=~ s/(.)\.[a-z]+$/$1/a;
local
$_
=
join
(
""
,
@_
);
s/\n\z//s;
printf
STDERR
"%s:%d: %s\n"
,
$fn
,
$lno
,
$_
;
}
sub
import
{
t/t_TestCommon.pm view on Meta::CPAN
112113114115116117118119120121122123124125126127128129130131132use
Config;
BEGIN {
unless
(Cwd::abs_path(__FILE__) =~ /Data-Dumper-Interp/) {
# unless we are testing DDI
#$Data::Dumper::Interp::Foldwidth = undef; # use terminal width
$Data::Dumper::Interp::Useqq
=
"controlpics:unicode"
;
}
}
sub
bug(@) {
@_
=(
"BUG FOUND:"
,
@_
);
goto
&Carp::confess
}
# Parse manual-testing args from @ARGV
my
@orig_ARGV
=
@ARGV
;
our
(
$debug
,
$verbose
,
$silent
,
$savepath
,
$nobail
,
$nonrandom
,
%dvs
);
Getopt::Long::Configure(
"pass_through"
);
GetOptions(
"d|debug"
=>
sub
{
$debug
=
$verbose
=1;
$silent
=0 },
"s|silent"
=> \
$silent
,
"savepath=s"
=> \
$savepath
,
t/t_TestCommon.pm view on Meta::CPAN
206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
_start_silent()
unless
$debug
;
}
die
"Unhandled tag "
,
keys
(
%tags
)
if
keys
(
%tags
);
# chain to Exporter to export any other importable items
goto
&Exporter::import
}
# Avoid turning on Test2 if not otherwise used...
sub
dprint(@) {
(
@_
)
if
$debug
};
sub
dprintf($@) {
printf
(
$_
[0],
@_
[1..
$#_
])
if
$debug
};
sub
arrays_eq($$) {
my
(
$a
,
$b
) =
@_
;
return
0
unless
@$a
==
@$b
;
for
(
my
$i
=0;
$i
<=
$#$a
;
$i
++) {
return
0
unless
$a
->[
$i
] eq
$b
->[
$i
];
}
return
1;
}
sub
hash_subset($@) {
my
(
$hash
,
@keys
) =
@_
;
return
undef
if
!
defined
$hash
;
return
{
map
{
exists
(
$hash
->{
$_
}) ? (
$_
=>
$hash
->{
$_
}) : () }
@keys
}
}
# string_to_tempfile($string, args => for-mytempfile)
# string_to_tempfile($string, pseudo_template) # see mytempfile
#
sub
string_to_tempfile($@) {
my
(
$string
,
@tfargs
) =
@_
;
my
(
$fh
,
$path
) = mytempfile(
@tfargs
);
dprint
"> Creating $path\n"
;
$fh
$string
;
$fh
->flush;
seek
(
$fh
,0,0) or
die
"seek $path : $!"
;
wantarray
? (
$path
,
$fh
) :
$path
}
# Run a Perl script in a sub-process.
t/t_TestCommon.pm view on Meta::CPAN
254255256257258259260261262263264265266267268269270271272273274# formats will be predictable for testing.
#
# This is usually enclosed in Capture::Tiny::capture { ... }
#
# ==> IMPORTANT: Be sure STDOUT/ERR has :encoding(...) set beforehand
# because Capture::Tiny will decode captured output the same way.
# Otherwise wide chars will be corrupted
#
#
require
Carp::Always;
sub
run_perlscript(@) {
my
@tfs
;
# keep in scope until no longer needed
my
@perlargs
= (
"-CIOE"
,
@_
);
@perlargs
= ((
map
{
"-I$_"
}
@INC
),
@perlargs
);
#unshift @perlargs, "-MCarp=verbose" if $Carp::Verbose;
#unshift @perlargs, "-MCarp::Always=verbose" if $Carp::Always::Verbose;
##This breaks no-internals-mentioned (AUTHOR_TESTS) in Spreadsheet::Edit
## For unknown reason some smokers running older perls die with
## "...undef value as a subroutine reference at site_perl/5.20.3/TAP/Harness.pm line 612
## So trying to see what is happening...
t/t_TestCommon.pm view on Meta::CPAN
334335336337338339340341342343344345346347348349350351352353354#
# Therefore tests can be simply wrapped in silent{...} or the entire
# program via the ':silent' tag; however any "Silence expected..." diagnostics
# will appear at the end, perhaps long after the specific test case which
# emitted the undesired output.
my
(
$orig_stdOUT
,
$orig_stdERR
,
$orig_DIE_trap
);
my
(
$inmem_stdOUT
,
$inmem_stdERR
) = (
""
,
""
);
my
$silent_mode
;
my
$start_silent_loc
=
""
;
sub
_finish_silent() {
confess
"not in silent mode"
unless
$silent_mode
;
close
STDERR;
open
(STDERR,
">>&"
,
$orig_stdERR
) or
exit
(198);
close
STDOUT;
open
(STDOUT,
">>&"
,
$orig_stdOUT
) or
die
"orig_stdOUT: $!"
;
$SIG
{__DIE__} =
$orig_DIE_trap
;
$silent_mode
= 0;
# The in-memory files hold octets; decode them before printing
# them out (when they will be re-encoded for the user's terminal).
my
$errmsg
;
t/t_TestCommon.pm view on Meta::CPAN
357358359360361362363364365366367368369370371372373374375376377
STDOUT decode(
"utf8"
,
$inmem_stdOUT
, FB_PERLQQ|LEAVE_SRC);
$errmsg
//=
"Silence expected on STDOUT"
;
}
if
(
$inmem_stdERR
ne
""
) {
STDERR
"--- saved STDERR ---\n"
;
STDERR decode(
"utf8"
,
$inmem_stdERR
, FB_PERLQQ|LEAVE_SRC);
$errmsg
=
$errmsg
?
"$errmsg and STDERR"
:
"Silence expected on STDERR"
;
}
defined
(
$errmsg
) ?
$errmsg
.
" at $start_silent_loc\n"
:
undef
;
}
sub
_start_silent() {
confess
"nested silent treatments not supported"
if
$silent_mode
;
$silent_mode
= 1;
for
(
my
$N
=0; ;++
$N
) {
my
(
$pkg
,
$file
,
$line
) =
caller
(
$N
);
$start_silent_loc
=
"$file line $line"
,
last
if
$pkg
ne __PACKAGE__;
}
$orig_DIE_trap
=
$SIG
{__DIE__};
$SIG
{__DIE__} =
sub
{
t/t_TestCommon.pm view on Meta::CPAN
386387388389390391392393394395396397398399400401402403404405406
close
STDOUT;
open
(STDOUT,
">"
, \
$inmem_stdOUT
) or
die
"redir STDOUT: $!"
;
binmode
(STDOUT);
binmode
(STDOUT,
":utf8"
);
my
@ERR_layers
=
grep
{
$_
ne
"unix"
} PerlIO::get_layers(
*STDERR
,
output
=>1);
open
(
$orig_stdERR
,
">&"
, \
*STDERR
) or
die
"dup STDERR: $!"
;
close
STDERR;
open
(STDERR,
">"
, \
$inmem_stdERR
) or
die
"redir STDERR: $!"
;
binmode
(STDERR);
binmode
(STDERR,
":utf8"
);
}
sub
silent(&) {
my
$wantarray
=
wantarray
;
my
$code
=
shift
;
_start_silent();
my
@result
=
do
{
if
(
defined
$wantarray
) {
return
(
$wantarray
?
$code
->() :
scalar
(
$code
->()) );
}
$code
->();
my
$dummy_result
;
# so previous call has null context
};
t/t_TestCommon.pm view on Meta::CPAN
433434435436437438439440441442443444445446447448449450451452
(
$testee_top_module
= $1) =~ s/-/::/g;
last
}
if
(-e (
my
$p
=
$path
->child(
"MYMETA.json"
))) {
$testee_top_module
= JSON->new->decode(
$p
->slurp_utf8())->{name};
$testee_top_module
=~ s/-/::/g;
last
;
}
}
sub
verif_no_internals_mentioned($) {
# croaks if references found
my
$original
=
shift
;
oops
"This may not be used except in a CPAN distribution tree"
unless
$testee_top_module
;
return
if
$Carp::Verbose
;
local
$_
=
$original
;
# Ignore glob refs like \*{"..."}
s/(?<!\\)\\\*\{
"[^"
]*"\}//g;
t/t_TestCommon.pm view on Meta::CPAN
474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
my
$start
= $-[1];
# offset of start of item
my
$end
= $+[1];
# offset of end+1
substr
(
$_
,
$start
,0) =
"HERE>>>"
;
substr
(
$_
,
$end
+7,0) =
"<<<THERE"
;
local
$Carp::Verbose
= 0;
# no full traceback
$Carp::CarpLevel
++;
croak
$msg
,
":\n«$_»\n"
;
}
1
# return true result if we don't croak
}
sub
show_empty_string(_) {
$_
[0] eq
""
?
"<empty string>"
:
$_
[0]
}
sub
show_white(_) {
# show whitespace which might not be noticed
local
$_
=
shift
;
return
"(Is undef)"
unless
defined
;
s/\t/<tab>/sg;
s/( +)$/
"<space>"
x
length
($1)/seg;
# only trailing spaces
s/\R/<newline>\n/sg;
show_empty_string
$_
}
#our $showstr_maxlen = 300;
our
$showstr_maxlen
= INT_MAX;
our
@quotes
= (
"«"
,
"»"
);
#our @quotes = ("<<", ">>");
sub
rawstr(_) {
# just the characters in French Quotes (truncated)
# Show spaces visibly
my
$text
=
$_
[0];
##$text =~ s/ /\N{MIDDLE DOT}/gs;
$quotes
[0].(
length
(
$text
)>
$showstr_maxlen
?
substr
(
$text
,0,
$showstr_maxlen
-3).
"..."
:
$text
).
$quotes
[1]
}
# Show controls as single-charcter indicators like DDI's "controlpics",
# with the whole thing in French Quotes. Truncate if huge.
sub
showcontrols(_) {
local
$_
=
shift
;
s/\n/\N{U+2424}/sg;
# a special NL glyph
s/[\x{00}-\x{1F}]/
chr
(
ord
($&)+0x2400 ) /aseg;
rawstr
}
# Show controls as traditional \t \n etc. if possible
sub
showstr(_) {
if
(
defined
&Data::Dumper::Interp::visnew
) {
return
visnew->Useqq(
"unicode"
)->vis(
shift
);
}
else
{
# I don't want to require Data::Dumper::Interp to be
# loaded although it will be if t_Common.pm was used also.
return
showcontrols(
shift
);
}
}
# Show the raw string in French Quotes.
# If STDOUT is not UTF-8 encoded, also show D::D hex escapes
# so we can still see something useful in output from non-Unicode platforms.
sub
displaystr($) {
my
(
$input
) =
@_
;
return
"undef"
if
!
defined
(
$input
);
local
$_
;
state
$utf8_output
=
grep
/utf.?8/i, PerlIO::get_layers(
*STDOUT
,
output
=>1);
my
$r
= rawstr(
$input
);
if
(!
$utf8_output
&&
$input
=~ /[^[:
:]]/a) {
# Data::Dumper will show 'wide' characters as hex escapes
my
$dd
= Data::Dumper->new([
$input
])->Useqq(1)->Terse(1)->Indent(0)->Dump;
if
(
$dd
ne
$input
&&
$dd
ne
"\"$input\""
) {
$r
.=
"\nD::D->$dd"
;
}
}
$r
}
sub
fmt_codestring($;$) {
# returns list of lines
my
(
$str
,
$prefix
) =
@_
;
$prefix
//=
"line "
;
my
$i
;
map
{
sprintf
"%s%2d: %s\n"
,
$prefix
,++
$i
,
$_
} (
split
/\n/,
$_
[0]);
}
# These wrappers add the caller's line number to the test description
# so they show when successful tests log their name.
# This is only visible with using "perl -Ilib t/xxx.t"
# not with 'prove -l' and so mostly pointless!
sub
t_ok($;$) {
my
(
$isok
,
$test_label
) =
@_
;
my
$lno
= (
caller
)[2];
$test_label
= (
$test_label
//
""
) .
" (line $lno)"
;
@_
= (
$isok
,
$test_label
);
goto
&Test2::V0::ok
;
# show caller's line number
}
sub
ok_with_lineno($;$) {
goto
&t_ok
};
sub
t_is($$;$) {
my
(
$got
,
$exp
,
$test_label
) =
@_
;
my
$lno
= (
caller
)[2];
$test_label
= (
$test_label
//
$exp
//
"undef"
) .
" (line $lno)"
;
@_
= (
$got
,
$exp
,
$test_label
);
goto
&Test2::V0::is
;
# show caller's line number
}
sub
is_with_lineno($$;$) {
goto
&t_is
}
sub
t_like($$;$) {
my
(
$got
,
$exp
,
$test_label
) =
@_
;
my
$lno
= (
caller
)[2];
$test_label
= (
$test_label
//
$exp
) .
" (line $lno)"
;
@_
= (
$got
,
$exp
,
$test_label
);
goto
&Test2::V0::like
;
# show caller's line number
}
sub
like_with_lineno($$;$) {
goto
&t_like
}
sub
_mycheck_end($$$) {
my
(
$errmsg
,
$test_label
,
$ok_only_if_failed
) =
@_
;
return
if
$ok_only_if_failed
&& !
$errmsg
;
my
$lno
= (
caller
)[2];
&Test2::V0::diag
(
"**********\n${errmsg}***********\n"
)
if
$errmsg
;
@_
= ( !
$errmsg
,
$test_label
);
goto
&ok_with_lineno
;
}
# Nicer alternative to mycheck() when 'expected' is a literal string, not regex
sub
mycheckeq_literal($$$) {
my
(
$desc
,
$exp
,
$act
) =
@_
;
#confess "'exp' is not plain string in mycheckeq_literal" if ref($exp); #not re!
$exp
= show_white(
$exp
);
# stringifies undef
$act
= show_white(
$act
);
return
unless
$exp
ne
$act
;
my
$hposn
= 0;
my
$vposn
= 0;
for
(0..
length
(
$exp
)) {
my
$c
=
substr
(
$exp
,
$_
,1);
last
if
$c
ne
substr
(
$act
,
$_
,1);
t/t_TestCommon.pm view on Meta::CPAN
615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
.
"Expected:\n"
.displaystr(
$exp
).
"\n"
.
"Actual:\n"
.displaystr(
$act
).
"\n"
# + for opening « or << in the displayed str
.(
" "
x (
$hposn
+
length
(
$quotes
[0]))).
"^"
.(
$vposn
> 0 ?
"(line "
.(
$vposn
+1).
")\n"
:
"\n"
)
.
" at line "
, (
caller
(0))[2].
"\n"
) ;
goto
&Carp::confess
;
#Carp::confess(@_);
}
sub
expect1($$) {
@_
= (
""
,
@_
);
goto
&mycheckeq_literal
;
}
# Convert a literal "expected" string which contains things which are
# represented differently among versions of Perl and/or Data::Dumper
# into a regex which works with all versions.
# As of 1/1/23 the input string is expected to be what Perl v5.34 produces.
our
$bs
=
'\\'
;
# a single backslash
sub
_expstr2restr($) {
local
$_
=
shift
;
confess
"bug"
if
ref
(
$_
);
return
$_
if
$_
eq
""
;
# In \Q *string* \E the *string* may not end in a backslash because
# it would be parsed as (\\)(E) instead of (\)(\E).
# So change them to a unique token and later replace problematic
# instances with ${bs} variable references.
s/\\/<BS>/g;
$_
=
'\Q'
.
$_
.
'\E'
;
s
#([\$\@\%]+)# do{ local $_ = $1;
t/t_TestCommon.pm view on Meta::CPAN
671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
or
confess
"Problem with filehandle in input string <<$_>>"
;
#say "#XX fh AFTER : $_";
}
s/<BS>\\/\${bs}\\/g;
s/<BS>/\\/g;
#say "#XX FINAL : $_";
$_
}
sub
expstr2re($) {
my
$input
=
shift
;
my
$xdesc
;
# extra debug description of intermediates
my
$output
;
if
(
$input
!~ m
#qr/|"::#) {
# doesn't contain variable-representation items
$output
=
$input
;
$xdesc
=
""
;
}
else
{
my
$s
= _expstr2restr(
$input
);
my
$saved_dollarat
= $@;
my
$re
=
eval
"qr{$s}"
;
die
"$@ "
if
$@;
$@ =
$saved_dollarat
;
$xdesc
=
"**Orig match str :"
.displaystr(
$input
).
"\n"
.
"**Generated re str:"
.displaystr(
$s
).
"\n"
;
$output
=
$re
;
}
wantarray
? (
$xdesc
,
$output
) :
$output
}
# mycheck $test_desc, string_or_regex, result
sub
mycheck($$@) {
my
(
$desc
,
$expected_arg
,
@actual
) =
@_
;
local
$_
;
# preserve $1 etc. for caller
my
@expected
=
ref
(
$expected_arg
) eq
"ARRAY"
?
@$expected_arg
: (
$expected_arg
);
if
($@) {
local
$_
;
confess
"Eval error: $@\n"
unless
$@ =~ /fake/i;
# It's okay if $@ is "...Fake..."
}
confess
"zero 'actual' results"
if
@actual
==0;
confess
"ARE WE USING THIS FEATURE? (@actual)"
if
@actual
!= 1;
confess
"ARE WE USING THIS FEATURE? (@expected)"
if
@expected
!= 1;
t/t_TestCommon.pm view on Meta::CPAN
739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786#say "###EXP $expected";
}
else
{
unless
(
$expected
eq
$actual
) {
@_
= (
"TESTc FAILED: $desc"
,
$expected
,
$actual
);
goto
&mycheckeq_literal
}
}
}
}
sub
verif_eval_err(;$) {
# MUST be called on same line as the 'eval'
my
(
$msg_regex
) =
@_
;
my
@caller
=
caller
(0);
my
$ln
=
$caller
[2];
my
$fn
=
$caller
[1];
my
$ex
= $@;
confess
"expected error did not occur at $fn line $ln\n"
,
unless
$ex
;
if
(
$ex
!~ / at \Q
$fn
\E line
$ln
\.?(?:$|\R)/s) {
confess
"Got UN-expected err (not ' at $fn line $ln'):\n«$ex»\n"
,
"\n"
;
}
if
(
$msg_regex
&&
$ex
!~
qr/$msg_regex/
) {
confess
"Got UN-expected err (not matching $msg_regex) at $fn line $ln'):\n«$ex»\n"
,
"\n"
;
}
verif_no_internals_mentioned(
$ex
)
if
defined
$testee_top_module
;
dprint
"Got expected err: $ex\n"
;
}
sub
insert_loc_in_evalstr($) {
my
$orig
=
shift
;
my
(
$fn
,
$lno
) = (
caller
(0))[1,2];
#use Data::Dumper::Interp; say dvis '###insert_loc_in_evalstr $fn $lno';
"# line $lno \"$fn\"\n"
.
$orig
}
sub
timed_run(&$@) {
my
(
$code
,
$maxcpusecs
,
@codeargs
) =
@_
;
my
$getcpu
=
eval
{
do
{
() = (
&Time::HiRes::clock
());
\
&Time::HiRes::clock
;
}} //
sub
{
my
@t
=
times
;
$t
[0]+
$t
[1] };
dprint(
"Note: $@"
)
if
$@;
$@ =
""
;
# avoid triggering "Eval error" in mycheck();
t/t_TestCommon.pm view on Meta::CPAN
790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
if
(
wantarray
) {
@result
=
&$code
(
@codeargs
)}
else
{
$result
=
&$code
(
@codeargs
)};
my
$cpusecs
=
&$getcpu
() -
$startclock
;
confess
"TOOK TOO LONG ($cpusecs CPU seconds vs. limit of $maxcpusecs)\n"
if
$cpusecs
>
$maxcpusecs
;
if
(
wantarray
) {
return
@result
}
else
{
return
$result
};
}
# Copy a file if needed to prevent any possibilty of it being modified.
# Returns the original path if the file is read-only, otherwise the path
# of a temp copy.
sub
tmpcopy_if_writeable($) {
my
$path
=
shift
;
confess
"$path : $!"
unless
stat
(
$path
);
if
( (
stat
(_))[2] & 0222 ) {
my
(
$name
,
$suf
) = (basename(
$path
) =~ /^(.*?)((?:\.\w{1,4})?)$/);
(
undef
,
my
$tpath
) =
File::Temp::tempfile(
SUFFIX
=>
$suf
,
UNLINK
=> 1);
File::Copy::copy(
$path
,
$tpath
) or
die
"File::Copy $!"
;
return
$tpath
;
}
$path
}
sub
clean_capture_output($) {
my
$str
=
shift
;
# For some reason I can not track down, tests on Windows in VirtualBox sometimes emit
# this message. I think (unproven) that this occurs because the current directory
# is a VBox host-shared directory mounted read-only. But nobody should be writing
# to the cwd!
$str
=~ s/The media is
write
protected\S*\R//gs;
$str
}
sub
my_capture(&) {
my
(
$out
,
$err
,
@results
) =
&capture
(
$_
[0]);
return
( clean_capture_output(
$out
), clean_capture_output(
$err
),
@results
);
}
sub
my_capture_merged(&) {
my
(
$merged
,
@results
) =
&capture_merged
(
$_
[0]);
return
( clean_capture_output(
$merged
),
@results
);
}
sub
my_tee_merged(&) {
my
(
$merged
,
@results
) =
&tee_merged
(
$_
[0]);
return
( clean_capture_output(
$merged
),
@results
);
}
1;
t/t_dsUtils.pm view on Meta::CPAN
91011121314151617181920212223242526272829use
Capture::Tiny ();
require
PerlIO;
our
$progname
=
"diff_spreadsheets"
;
our
$progpath
=
"$Bin/../bin/$progname"
;
sub
runtest($$$$$$;@) {
my
(
$in1
,
$in2
,
$exp_out
,
$exp_err
,
$exp_exit
,
$desc
,
@extraargs
) =
@_
;
if
(state
$first_time
= 1) {
# Capture::Tiny will decode octets from the results according to whatever
# encoding was set for STDOUT or STDERR, and if not it won't decode.
# This corrupts wide characters unless enc is pre-set on those handles.
unless
(
grep
/utf.*8/i, PerlIO::get_layers(
*STDOUT
)) {
croak
"STDOUT does not have utf8 or encoding(UTF-8) enabled"
;
}
unless
(
grep
/utf.*8/i, PerlIO::get_layers(
*STDERR
)) {
croak
"STDERR does not have utf8 or encoding(UTF-8) enabled"
;
tlib/xmlstuff.pl view on Meta::CPAN
9101112131415161718192021222324252627282930313233343536373839use
Carp;
use
Data::Dumper::Interp;
use
XML::Twig ();
sub
encode_xml($$;$) {
my
(
$chars
,
$encoding
,
$desc
) =
@_
;
confess
"bug"
unless
defined
(
$chars
) &&
defined
(
$encoding
);
$chars
=~ s/(<\?xml[^\?]
*encoding
=
")([^"
]+)("[^\?]*\?>)/$1${encoding}$3/s
or confess
qq(Could not find <?xml ... encoding="..."?>)
,
(
$desc
?
" in $desc"
:
""
);
my
$octets
= encode(
$encoding
,
$chars
, Encode::FB_CROAK|Encode::LEAVE_SRC);
$octets
}
sub
decode_xml($;$) {
my
(
$octets
,
$desc
) =
@_
;
my
$chars
;
my
$encoding
;
if
(
length
(
$octets
) == 0) {
$chars
=
""
;
}
else
{
(
$encoding
) = (
$octets
=~ /<\?xml[^\?]
*encoding
=
"([^"
]+)"[^\?]*\?>/);
confess
qq(Could not find <?xml ... encoding="..."?>)
,
(
$desc
?
" in $desc"
:
""
)
unless
$encoding
;