#!/usr/bin/perl use warnings; use strict; use FindBin '$Bin'; use Test::More tests => 1 + 22 + (8+1146+14+715+495+8) + (13+13+13); BEGIN { use_ok('Mac::AppleEvents'); require "$Bin/helper.pl"; } use Mac::Types; use MacPerl 'MakePath'; my $fourcharcode = ['abcd', '1 ', "\0\0\0\0"]; my $stringdata = ['a', 'ab', 'abc', @$fourcharcode, 'abcde', 'abcdef', "this is some random text I am just gonna add here OK?"]; # 15 my %types = ( # 3 * 5 = 15 typeEnumerated() => $fourcharcode, typeType() => $fourcharcode, typeKeyword() => $fourcharcode, typeApplSignature() => $fourcharcode, typeProperty() => $fourcharcode, # 9 typeChar() => $stringdata, # 2, 3 * 5 = 17 typeBoolean() => [0, 1], typeShortInteger() => [0, 123, -1234], typeInteger() => [0, -2**24, 2**31-1], typeShortFloat() => [0, 123.45, -1234.56], typeFloat() => [0, 12345678.91, (-2**24 + .234234)], typeMagnitude() => [0, 2**32-1, 2**31], # 1 typeFSS() => ['/System/Library/CoreServices/Finder.app'], typeQDRectangle() => [ [1, 240, 320, 2000] ], typeRGBColor() => [ [65535, 0, 0] ], # qdrt, cRGB, STR, STR# ); # = 44 SKIP: { # skip "Basic AEDesc tests", 22; my $desc = AEDesc->new(typeChar); is(ref($desc), 'AEDesc', 'Create AEDesc'); is($desc->type, typeChar, 'Check type'); ok(!defined($desc->get), 'No data'); my $hand = Handle->new('something'); is($desc->type(typeType), typeType, 'Change type'); ok(!defined($desc->get), 'No data'); is(ref($hand), 'Handle', 'Create handle'); ok($desc->data($hand), 'Add handle'); is($desc->get, 'some', 'Check value'); # truncated due to typeType is($desc->type(typeChar), typeChar, 'Change type'); # 6 * 2 = 12 for my $i (0, 1, 10, 100, 1000, 10000) { ok($desc->data(Handle->new($i)), 'Add handle'); is($desc->get, $i, 'Check value'); } ok(AEDisposeDesc($desc), 'Dispose'); } SKIP: { # skip "AEDesc and AEList tests", 8+1146+14+715+495+8; # 8 my $list = AECreateList('', 0); is($list->type, typeAEList, 'Create AEList'); my $reco = AECreateList('', 1); is($reco->type, typeAERecord, 'Create AERecord'); my $list2 = AECreateList('', 0); is($list2->type, typeAEList, 'Create AEList'); my $reco2 = AECreateList('', 1); is($reco2->type, typeAERecord, 'Create AERecord'); my $lists = AEStream->new; is(ref $lists, 'AEStream', 'Create AEStream list'); ok($lists->OpenList, 'OpenList'); my $recos = AEStream->new; is(ref $recos, 'AEStream', 'Create AEStream list'); ok($recos->OpenRecord, 'OpenRecord'); my($listg_fmt, $recog_fmt, @g_param) = ('', ''); # 44 * 4 * 5 = 880, + 44, + 33 * 4 = 132, + 15 * 6 = 90 = 1146 for my $type (sort keys %types) { my $data = $types{$type}; for my $datum (@$data) { my $packed = exists($MacPack{$type}) ? MacPack($type, (ref $datum ? @$datum : $datum)) : $datum; my $desc1 = AEDesc->new($type, $packed); my $desc2 = AEKeyDesc->new($type, $type, $packed); my $desc3 = AECreateDesc($type, $packed); my $desc4 = AEDuplicateDesc($desc1); # http://developer.apple.com/technotes/tn/tn2045.html my $builddata = $datum; my $lit = 0; my $skip = 0; my $hand = 0; if ($type eq typeFSS) { $builddata = $packed; } elsif ($datum =~ /\0/) { $builddata = MakeHex($datum); $lit = 1; } elsif ($type eq typeType) { $builddata = Handle->new(MakeFourChar($datum)); $hand = 1; } elsif ($type eq typeKeyword) { $builddata = MakeFourChar($datum); } elsif ($type eq typeApplSignature || $type eq typeProperty || $type eq typeEnumerated) { $builddata = "'$datum'"; $lit = 1; } elsif ($type eq typeBoolean) { $builddata = MakeNumHex($datum); $lit = 1; } elsif ($type eq typeShortInteger || $type eq typeInteger) { $lit = 1; } elsif (SkipType($type)) { # no idea why this doesn't work, oh well $skip = 1; } my $desc5 = $skip ? '' : $lit ? AEBuild("$type($builddata)") : $hand ? AEBuild("$type(\@@)", $builddata) : AEBuild("$type(\@)", $builddata); # http://developer.apple.com/technotes/tn/tn2046.html my $stream = AEStream->new; $stream->WriteDesc($type, $packed); my $desc6 = $stream->Close; #diag("$type: $datum"); if ($datum eq $data->[-1]) { ok(AEPut($list, AECountItems($list)+1, $type, $packed), 'AEPut'); ok(AEPutKey($reco, $type, $type, $packed), 'AEPutKey'); ok(AEPutDesc($list2, AECountItems($list2)+1, $desc4), 'AEPutDesc'); ok(AEPutKeyDesc($reco2, $type, $desc4), 'AEPutKeyDesc'); ok($lists->WriteDesc($type, $packed), 'WriteDesc list'); ok($recos->WriteKeyDesc($type, $type, $packed), 'WriteKeyDesc record'); unless ($skip) { if ($lit) { $listg_fmt .= "$type($builddata), "; $recog_fmt .= "$type : $type($builddata), "; } else { my $at = $hand ? '@@' : '@'; $listg_fmt .= "$type($at), "; $recog_fmt .= "$type : $type($at), "; push @g_param, $builddata; } } } #diag("$type($builddata)"); #diag(AEPrint($desc4)); #diag(AEPrint($desc5)) if $desc5; for my $desc ($desc1, $desc2, $desc3, $desc4, $desc5, $desc6) { next unless $desc; CheckDesc($desc, $type, $datum); } } } # 14 s/, $// for ($listg_fmt, $recog_fmt); my $listg = AEBuild("[$listg_fmt]", @g_param); is($listg->type, typeAEList, 'Create AEList'); my $recog = AEBuild("{$recog_fmt}", @g_param); is($recog->type, typeAERecord, 'Create AERecord'); ok($lists->CloseList, 'CloseList'); ok($recos->CloseRecord, 'CloseRecord'); ok(my $list3 = $lists->Close, 'Close list'); ok(my $reco3 = $recos->Close, 'Close record'); my $count = scalar keys %types; for my $L ($list, $list2, $list3, $reco, $reco2, $reco3) { is(AECountItems($L), $count, 'Count list items'); } my $countg = $count - 5; for my $L ($listg, $recog) { is(AECountItems($L), $countg, 'Count list items'); } # 15 * 13 * 3 = 585, + 13 * 10 = 715 my $i = 0; my $g = 0; for my $type (sort keys %types) { $i++; $g++; my $datum = $types{$type}[-1]; #diag("AEList: $type: $datum"); my $j = 0; for my $L ($list, $list2, $list3, $listg) { my $k = $i; if (++$j == 4) { #diag("ok!: $type: $g"); if (SkipType($type)) { $g--; next; } $k = $g; } my $desc = AEGetNthDesc($L, $k); CheckDesc($desc, $type, $datum); } $j = 0; for my $L ($reco, $reco2, $reco3, $recog) { my $k = $i; if (++$j == 4) { #diag("ok!!: $type: $g"); if (SkipType($type)) { next; } $k = $g; } my $desc = AEGetKeyDesc($L, $type); CheckDesc($desc, $type, $datum); # same as above, but fetch by index ($desc, my($key)) = AEGetNthDesc($L, $k); is($key, $type, 'Check key'); CheckDesc($desc, $type, $datum); } } # 15 * 11 * 3 = 495 $i = 0; for my $type (sort keys %types) { $i++; my $datum = $types{$type}[-1]; #diag("AEDelete: $type: $datum"); for my $L ($list, $list2, $list3) { my $desc = AEGetNthDesc($L, 1); CheckDesc($desc, $type, $datum); AEDeleteItem($L, 1); my $tab = AECountItems($L); cmp_ok($tab, '==', $count-$i, 'Count items remaining'); } for my $L ($reco, $reco2, $reco3) { my($desc, $key) = AEGetNthDesc($L, 1); is($key, $type, 'Check key'); CheckDesc($desc, $type, $datum); AEDeleteItem($L, 1); my $tab = AECountItems($L); $tab =~ s/\D+//; is($tab, $count-$i, 'Count items remaining'); } } # 8 for my $L ($list, $list2, $list3, $listg, $reco, $reco2, $reco3, $recog) { ok(AEDisposeDesc($L), 'Dispose list'); } } SKIP: { # skip "AECoerce tests", 13+13+13; my $string = "abcdef"; my $desc = AEDesc->new(typeChar, $string); my $desc2 = AECoerceDesc($desc, typeUnicodeText); my $desc3 = AECoerce(typeChar, $string, typeUnicodeText); # 13 CheckDesc($desc, typeChar, $string); CheckRef($desc2, typeUnicodeText); CheckType($desc2, typeUnicodeText); is(length($desc2->get), 2*length($string), 'Length check'); CheckRef($desc3, typeUnicodeText); CheckType($desc3, typeUnicodeText); is(length($desc3->get), 2*length($string), 'Length check'); is($desc2->get, $desc3->get, 'Value check'); CheckDispose($desc2); CheckDispose($desc3); my $keyw = "abcd"; $desc = AEDesc->new(typeKeyword, $keyw); $desc2 = AECoerceDesc($desc, typeChar); $desc3 = AECoerce(typeKeyword, $keyw, typeChar); # 13 CheckDesc($desc, typeKeyword, $keyw); is($desc2->get, $desc3->get, 'Value check'); CheckDesc($desc2, typeChar, $keyw); CheckDesc($desc3, typeChar, $keyw); my $num = 2**18; my $num2 = $num + .45; $desc = AEDesc->new(typeFloat, MacPack(typeFloat, $num2)); $desc2 = AECoerceDesc($desc, typeInteger); $desc3 = AECoerce(typeFloat, MacPack(typeFloat, $num2), typeInteger); # 13 CheckDesc($desc, typeFloat, $num2); CheckRef($desc2, typeInteger); CheckType($desc2, typeInteger); is($desc2->get, $num, 'Value check'); CheckRef($desc3, typeInteger); CheckType($desc3, typeInteger); is($desc3->get, $num, 'Value check'); is($desc2->get, $desc3->get, 'Value check'); CheckDispose($desc2); CheckDispose($desc3); } sub MakeFourChar { pack "N", unpack "L", $_[0]; } sub MakeHex { '$' . join('', map { sprintf("%02X", ord) } split //, MakeFourChar($_[0])) . '$'; } sub MakeNumHex { my $hex = '$' . sprintf("%02X", $_[0]) . '$'; if (length($hex) % 2) { $hex =~ s/^\$/\$0/; } return $hex; } sub SkipType { my($type) = @_; return 1 if $type eq typeShortFloat || $type eq typeFloat || $type eq typeMagnitude || $type eq typeQDRectangle || $type eq typeRGBColor; return 0; } =pod =head1 TODO =over 4 * location/range/comparison/logical? * AEBuild doesn't work with unsigned 32-bit or floats? * AEBuild doesn't automatically handle byteswapping of OSTypes * AEStream WriteData, same problem =back __END__