require
5.002;
use
vars
qw($VERSION @ISA @EXPORT)
;
$VERSION
=
'1.24'
;
@ISA
=
qw(Exporter)
;
@EXPORT
=
qw(check writeCheck writeInfo testCompare binaryCompare testVerbose notOK done)
;
my
$noTimeLocal
;
my
$rtnCode
= 0;
sub
nearEnough($$);
sub
nearTime($$$$);
sub
formatValue($);
sub
writeInfo($$;$$$);
sub
notOK();
sub
binaryCompare($$)
{
my
(
$file1
,
$file2
) =
@_
;
my
$success
= 1;
open
(TESTFILE1,
$file1
) or
return
0;
unless
(
open
(TESTFILE2,
$file2
)) {
close
(TESTFILE1);
return
0;
}
binmode
(TESTFILE1);
binmode
(TESTFILE2);
my
(
$buf1
,
$buf2
);
while
(
read
(TESTFILE1,
$buf1
, 65536)) {
read
(TESTFILE2,
$buf2
, 65536) or
$success
= 0,
last
;
$buf1
eq
$buf2
or
$success
= 0,
last
;
}
read
(TESTFILE2,
$buf2
, 65536) and
$success
= 0;
close
(TESTFILE1);
close
(TESTFILE2);
return
$success
}
sub
testCompare($$$;$)
{
my
(
$stdfile
,
$testfile
,
$testnum
,
$keep
) =
@_
;
my
$success
= 0;
my
$linenum
;
my
$oldSep
= $/;
$/ =
"\x0a"
;
if
(
open
(FILE1,
$stdfile
)) {
if
(
open
(FILE2,
$testfile
)) {
$success
= 1;
my
(
$line1
,
$line2
);
my
$linenum
= 0;
my
$skip
= 0;
for
(;;) {
$line1
= <FILE1>
unless
$skip
== 1;
last
unless
defined
$line1
;
++
$linenum
;
$line2
= <FILE2>
unless
$skip
== 2;
$skip
= 0;
if
(
defined
$line2
) {
next
if
$line1
eq
$line2
;
next
if
nearEnough(
$line1
,
$line2
);
if
(
$line1
=~ /Warning: IPTCDigest is not current/ and
not
eval
'require Digest::MD5'
)
{
$skip
= 2;
next
;
}
elsif
(
$line2
=~ /Warning: IPTCDigest is not current/ and
not
eval
'require Digest::MD5'
)
{
$skip
= 1;
next
;
}
}
$success
= 0;
last
;
}
if
(
$success
) {
$line2
= <FILE2>;
if
(
$line2
) {
++
$linenum
;
$success
= 0;
}
}
unless
(
$success
) {
warn
"\n Test $testnum differs beginning at line $linenum:\n"
;
defined
$line1
or
$line1
=
'(null)'
;
defined
$line2
or
$line2
=
'(null)'
;
chomp
(
$line1
,
$line2
);
warn
qq{ Test gave: "$line2"\n}
;
warn
qq{ Should be: "$line1"\n}
;
}
close
(FILE2);
}
close
(FILE1);
}
$/ =
$oldSep
;
$success
and not
$keep
and
unlink
$testfile
;
return
$success
}
sub
nearEnough($$)
{
my
(
$line1
,
$line2
) =
@_
;
return
1
if
$line1
=~ /^(.
*ExifTool
.*)\b\d{1,2}\.\d{2}\b(.*)/s and
(
$line2
eq
"$1$Image::ExifTool::VERSION$Image::ExifTool::RELEASE$2"
or
$line2
eq
"$1$Image::ExifTool::VERSION$2"
);
return
1
if
$line1
=~ /(File\s?(Modif.
*Date
|Access\s?Date|Inode\s?Change\s?Date|Permissions))/ and
(
$line2
=~ /$1/ or
$line2
=~ /File\s?Creat.
*Date
/);
return
1
if
$line1
=~ /Current IPTC Digest/ and
$line2
=~ /Current IPTC Digest: (0|
not
eval
'require Digest::MD5'
;
my
@toks1
=
split
/\s+/,
$line1
;
my
@toks2
=
split
/\s+/,
$line2
;
my
$lenChanged
= 0;
my
$i
;
for
(
$i
=0; ; ++
$i
) {
return
1
if
$i
>=
@toks1
and
$i
>=
@toks2
;
my
$tok1
=
$toks1
[
$i
];
my
$tok2
=
$toks2
[
$i
];
last
unless
defined
$tok1
and
defined
$tok2
;
next
if
$tok1
eq
$tok2
;
if
(
$tok1
=~ /\[(\.{3}|snip)\]$/ or
$tok2
=~ /\[(\.{3}|snip)\]$/) {
return
1
if
$tok1
=~ /^[-+]?\d+\./ or
$tok2
=~/^[-+]?\d+\./;
return
$lenChanged
}
if
(
$tok1
=~ /^(\d{2}|\d{4}):\d{2}:\d{2}/ and
$tok2
=~ /^(\d{2}|\d{4}):\d{2}:\d{2}/ and
{
unless
(
$noTimeLocal
) {
warn
"Ignored time difference(s) because Time::Local is not installed\n"
;
$noTimeLocal
= 1;
}
next
;
}
elsif
(
$tok1
=~ /^(\d{2}:\d{2}:\d{2}(?:\.\d+)?)(Z|[-+]\d{2}:\d{2})$/i) {
my
$time
= $1;
next
if
$tok2
=~ /^(\d{2}:\d{2}:\d{2}(?:\.\d+)?)(Z|[-+]\d{2}:\d{2})$/i and
$time
eq $1;
last
unless
$i
and
$toks1
[
$i
-1] =~ /^\d{4}:\d{2}:\d{2}$/ and
$toks2
[
$i
-1] =~ /^\d{4}:\d{2}:\d{2}$/;
$tok1
=
$toks1
[
$i
-1] .
' '
.
$tok1
;
$tok2
=
$toks2
[
$i
-1] .
' '
.
$tok2
;
last
unless
nearTime(
$tok1
,
$tok2
,
$line1
,
$line2
);
}
elsif
(
$tok1
=~ /^\d{4}:\d{2}:\d{2}$/ and
$tok2
=~ /^\d{4}:\d{2}:\d{2}$/ and
defined
$toks1
[
$i
+1] and
defined
$toks2
[
$i
+1] and
$toks1
[
$i
+1] =~ /^(\d{2}:\d{2}:\d{2}(?:\.\d+)?)(Z|[-+]\d{2}:\d{2})$/i and
$toks2
[
$i
+1] =~ /^(\d{2}:\d{2}:\d{2}(?:\.\d+)?)(Z|[-+]\d{2}:\d{2})$/i)
{
++
$i
;
$tok1
.=
' '
.
$toks1
[
$i
];
$tok2
.=
' '
.
$toks2
[
$i
];
last
unless
nearTime(
$tok1
,
$tok2
,
$line1
,
$line2
);
}
elsif
(
$tok1
=~ s/(\.
$tok2
=~ s/(\.
last
if
$tok1
ne
$tok2
;
}
else
{
if
(
$tok1
=~ s/([^\d.]+)$//) {
my
$a
= $1;
last
unless
$tok2
=~ s/\Q
$a
\E$//;
}
if
(
$tok1
=~ s/^(\d+:\d+:)//) {
my
$a
= $1;
last
unless
$tok2
=~ s/^\Q
$a
//;
}
if
(
$tok1
=~ s/^'//) {
last
unless
$tok2
=~ s/^'//;
}
last
unless
Image::ExifTool::IsFloat(
$tok1
) and
Image::ExifTool::IsFloat(
$tok2
);
last
if
$tok1
== 0 or
$tok2
== 0;
if
(
abs
((
$tok1
-
$tok2
)/(
$tok1
+
$tok2
)) > 1e-5) {
my
(
$int1
,
$int2
);
(
$int1
=
$tok1
) =~
tr
/0-9//dc;
(
$int2
=
$tok2
) =~
tr
/0-9//dc;
my
$dlen
=
length
(
$int1
) -
length
(
$int2
);
if
(
$dlen
> 0) {
$int2
.=
'0'
x
$dlen
;
}
elsif
(
$dlen
< 0) {
$int1
.=
'0'
x (-
$dlen
);
}
last
if
abs
(
$int1
-
$int2
) > 1.00001;
}
}
$lenChanged
= 1
if
length
(
$tok1
) ne
length
(
$tok2
);
}
return
0;
}
sub
nearTime($$$$)
{
my
(
$tok1
,
$tok2
,
$line1
,
$line2
) =
@_
;
my
$t1
= Image::ExifTool::GetUnixTime(
$tok1
,
'local'
) or
return
0;
my
$t2
= Image::ExifTool::GetUnixTime(
$tok2
,
'local'
) or
return
0;
my
$td
=
$t2
-
$t1
;
if
(
$td
) {
return
0
unless
$^O eq
'mirbsd'
and
$td
< 0 and
$td
> -120;
warn
"\n Ignoring $td second error due to MirBSD leap-second \"feature\":\n"
;
chomp
(
$line1
,
$line2
);
warn
qq{ Test gave: "$line2"\n}
;
warn
qq{ Should be: "$line1"\n}
;
}
return
1;
}
sub
formatValue($)
{
local
$_
;
my
$val
=
shift
;
my
(
$str
,
@a
);
if
(
ref
$val
eq
'SCALAR'
) {
if
(
$$val
=~ /^Binary data/) {
$str
=
"($$val)"
;
}
else
{
$str
=
'(Binary data '
.
length
(
$$val
) .
' bytes)'
;
}
}
elsif
(
ref
$val
eq
'ARRAY'
) {
foreach
(
@$val
) {
push
@a
, formatValue(
$_
);
}
$str
=
'['
.
join
(
','
,
@a
) .
']'
;
}
elsif
(
ref
$val
eq
'HASH'
) {
my
$key
;
foreach
$key
(Image::ExifTool::OrderedKeys(
$val
)) {
push
@a
,
$key
.
'='
. formatValue(
$$val
{
$key
});
}
$str
=
'{'
.
join
(
','
,
@a
) .
'}'
;
}
elsif
(
defined
$val
) {
(
$str
=
$val
) =~
tr
/\x0a\x0d/;/;
$str
=~
tr
/\x01-\x1f\x7f/./;
$str
=~ s/\x00//g;
}
else
{
$str
=
''
;
}
return
$str
;
}
sub
check($$$;$$$)
{
my
$exifTool
=
shift
if
ref
$_
[0] ne
'HASH'
;
my
(
$info
,
$testname
,
$testnum
,
$stdnum
,
$topGroup
) =
@_
;
return
0
unless
$info
;
$stdnum
=
$testnum
unless
defined
$stdnum
;
my
$testfile
=
"t/${testname}_$testnum.failed"
;
my
$stdfile
=
"t/${testname}_$stdnum.out"
;
open
(FILE,
">$testfile"
) or
return
0;
my
$oldSep
= $\;
$\ =
"\x0a"
;
my
@tags
;
if
(
$exifTool
) {
if
(
$$exifTool
{NO_SORT}) {
@tags
=
$exifTool
->GetFoundTags();
}
else
{
@tags
=
$exifTool
->GetTagList(
$info
,
'Group0'
);
}
}
else
{
@tags
=
sort
keys
%$info
;
}
foreach
(
@tags
) {
my
$val
= formatValue(
$$info
{
$_
});
if
(
$exifTool
) {
my
@groups
=
$exifTool
->GetGroup(
$_
);
my
$groups
=
join
', '
,
@groups
[0..(
$topGroup
||2)];
my
$tagID
=
$exifTool
->GetTagID(
$_
);
my
$desc
=
$exifTool
->GetDescription(
$_
);
print
FILE
"[$groups] $tagID - $desc: $val"
;
}
else
{
print
FILE
"$_: $val"
;
}
}
close
(FILE);
$\ =
$oldSep
;
return
testCompare(
$stdfile
,
$testfile
,
$testnum
);
}
sub
writeCheck($$$;$$$$)
{
my
(
$writeInfo
,
$testname
,
$testnum
,
$srcfile
,
$onlyWritten
,
$same
,
$ignore
) =
@_
;
$srcfile
or
$srcfile
=
"t/images/$testname.jpg"
;
my
(
$ext
) = (
$srcfile
=~ /\.(.+?)$/);
my
$testfile
=
"t/${testname}_${testnum}_failed.$ext"
;
my
$exifTool
= Image::ExifTool->new;
my
@tags
;
if
(
ref
$onlyWritten
eq
'ARRAY'
) {
@tags
=
@$onlyWritten
;
undef
$onlyWritten
;
}
foreach
(
@$writeInfo
) {
$exifTool
->SetNewValue(
@$_
);
push
@tags
,
$$_
[0]
if
$onlyWritten
;
}
unlink
$testfile
;
my
$ok
= writeInfo(
$exifTool
,
$srcfile
,
$testfile
,
$same
,
$ignore
);
my
$info
=
$exifTool
->ImageInfo(
$testfile
,{
Duplicates
=>1,
Unknown
=>1},
@tags
);
my
$rtnVal
= check(
$exifTool
,
$info
,
$testname
,
$testnum
);
return
0
unless
$ok
and
$rtnVal
;
unlink
$testfile
;
return
1;
}
sub
writeInfo($$;$$$)
{
my
(
$exifTool
,
$src
,
$dst
,
$same
,
$ignore
) =
@_
;
unlink
"${src}_exiftool_tmp"
if
not
defined
$dst
and not
ref
$src
;
my
$result
=
$exifTool
->WriteInfo(
$src
,
$dst
);
my
$err
=
''
;
$err
.=
" Error: WriteInfo() returned $result\n"
if
$result
!= (
$same
? 2 : 1);
my
$info
=
$exifTool
->GetInfo(
'Warning'
,
'Error'
);
foreach
(
sort
keys
%$info
) {
next
if
$ignore
and
$_
=~ /^Warning/;
my
$tag
= Image::ExifTool::GetTagName(
$_
);
$err
.=
" $tag: $$info{$_}\n"
;
}
return
1
unless
$err
;
warn
"\n$err"
;
return
0;
}
sub
testVerbose($$$$)
{
my
(
$testname
,
$testnum
,
$infile
,
$verbose
) =
@_
;
my
$testfile
=
"t/${testname}_$testnum"
;
return
0
unless
open
(TMPFILE,
">$testfile.tmp"
);
ImageInfo(
$infile
, {
Verbose
=>
$verbose
,
TextOut
=> \
*TMPFILE
});
close
(TMPFILE);
open
(TMPFILE,
"$testfile.tmp"
);
open
(TESTFILE,
">$testfile.failed"
);
my
$oldSep
= $\;
$\ =
"\x0a"
;
while
(<TMPFILE>) {
chomp
;
print
TESTFILE
$_
;
}
$\ =
$oldSep
;
close
(TESTFILE);
close
(TMPFILE);
unlink
(
"$testfile.tmp"
);
return
testCompare(
"$testfile.out"
,
"$testfile.failed"
,
$testnum
);
}
sub
notOK()
{
print
'not '
;
$rtnCode
= 1;
}
sub
done()
{
exit
$rtnCode
;
}
1;