6 push @INC, "::lib:$MacPerl::Architecture:" if $^O eq 'MacOS';
7 require Config; import Config;
8 if ($Config{'extensions'} !~ /\bXS\/APItest\b/) {
9 # Look, I'm using this fully-qualified variable more than once!
10 my $arch = $MacPerl::Architecture;
11 print "1..0 # Skip: XS::APItest was not built\n";
19 use Test::More 'no_plan';
21 BEGIN {use_ok('XS::APItest')};
26 sub test_delete_present;
27 sub test_delete_absent;
28 sub brute_force_exists;
30 sub test_fetch_present;
31 sub test_fetch_absent;
33 my $utf8_for_258 = chr 258;
34 utf8::encode $utf8_for_258;
36 my @testkeys = ('N', chr 198, chr 256);
37 my @keys = (@testkeys, $utf8_for_258);
40 utf8::downgrade $_, 1;
42 main_tests (\@keys, \@testkeys, '');
47 main_tests (\@keys, \@testkeys, ' [utf8 hash]');
51 tie %h, 'Tie::StdHash';
52 is (XS::APItest::Hash::store(\%h, chr 258, 1), undef);
54 ok (!exists $h{$utf8_for_258},
55 "hv_store doesn't insert a key with the raw utf8 on a tied hash");
59 my $strtab = strtab();
60 is (ref $strtab, 'HASH', "The shared string table quacks like a hash");
65 my $prefix = "Cannot modify shared string table in hv_";
66 my $what = $prefix . 'fetch';
67 like ($@, qr/^$what/,$what);
69 XS::APItest::Hash::store($strtab, 'Boom!', 1)
71 $what = $prefix . 'store';
72 like ($@, qr/^$what/, $what);
76 # DESTROY should be in there.
78 delete $strtab->{DESTROY};
80 $what = $prefix . 'delete';
81 like ($@, qr/^$what/, $what);
82 # I can't work out how to get to the code that flips the wasutf8 flag on
83 # the hash key without some ikcy XS
87 is_deeply([&XS::APItest::Hash::test_hv_free_ent], [2,2,1,1],
88 "hv_free_ent frees the value immediately");
89 is_deeply([&XS::APItest::Hash::test_hv_delayfree_ent], [2,2,2,1],
90 "hv_delayfree_ent keeps the value around until FREETMPS");
93 foreach my $in ("", "N", "a\0b") {
94 my $got = XS::APItest::Hash::test_share_unshare_pvn($in);
95 is ($got, $in, "test_share_unshare_pvn");
99 foreach ([\&XS::APItest::Hash::rot13_hash, \&rot13, "rot 13"],
100 [\&XS::APItest::Hash::bitflip_hash, \&bitflip, "bitflip"],
102 my ($setup, $mapping, $name) = @$_;
104 my %placebo = (a => 1, p => 2, i => 4, e => 8);
106 $hash{a}++; @hash{qw(p i e)} = (2, 4, 8);
108 test_U_hash(\%hash, \%placebo, [f => 9, g => 10, h => 11], $mapping,
115 ################################ The End ################################
118 my ($hash, $placebo, $new, $mapping, $message) = @_;
119 my @hitlist = keys %$placebo;
120 print "# $message\n";
122 my @keys = sort keys %$hash;
123 is ("@keys", join(' ', sort($mapping->(keys %$placebo))),
124 "uvar magic called exactly once on store");
128 my $victim = shift @hitlist;
129 is (delete $hash->{$victim}, delete $placebo->{$victim});
132 @keys = sort keys %$hash;
133 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
135 $victim = shift @hitlist;
136 is (XS::APItest::Hash::delete_ent ($hash, $victim,
137 XS::APItest::HV_DISABLE_UVAR_XKEY),
138 undef, "Deleting a known key with conversion disabled fails (ent)");
141 is (XS::APItest::Hash::delete_ent ($hash, $victim, 0),
142 delete $placebo->{$victim},
143 "Deleting a known key with conversion enabled works (ent)");
145 @keys = sort keys %$hash;
146 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
148 $victim = shift @hitlist;
149 is (XS::APItest::Hash::delete ($hash, $victim,
150 XS::APItest::HV_DISABLE_UVAR_XKEY),
151 undef, "Deleting a known key with conversion disabled fails");
154 is (XS::APItest::Hash::delete ($hash, $victim, 0),
155 delete $placebo->{$victim},
156 "Deleting a known key with conversion enabled works");
158 @keys = sort keys %$hash;
159 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
161 my ($k, $v) = splice @$new, 0, 2;
165 @keys = sort keys %$hash;
166 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
168 ($k, $v) = splice @$new, 0, 2;
169 is (XS::APItest::Hash::store_ent($hash, $k, $v), $v, "store_ent");
172 @keys = sort keys %$hash;
173 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
175 ($k, $v) = splice @$new, 0, 2;
176 is (XS::APItest::Hash::store($hash, $k, $v), $v, "store");
179 @keys = sort keys %$hash;
180 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
182 @hitlist = keys %$placebo;
183 $victim = shift @hitlist;
184 is (XS::APItest::Hash::fetch_ent($hash, $victim), $placebo->{$victim},
186 is (XS::APItest::Hash::fetch_ent($hash, $mapping->($victim)), undef,
187 "fetch_ent (missing)");
189 $victim = shift @hitlist;
190 is (XS::APItest::Hash::fetch($hash, $victim), $placebo->{$victim},
192 is (XS::APItest::Hash::fetch($hash, $mapping->($victim)), undef,
195 $victim = shift @hitlist;
196 ok (XS::APItest::Hash::exists_ent($hash, $victim), "exists_ent");
197 ok (!XS::APItest::Hash::exists_ent($hash, $mapping->($victim)),
198 "exists_ent (missing)");
200 $victim = shift @hitlist;
201 ok (XS::APItest::Hash::exists($hash, $victim), "exists");
202 ok (!XS::APItest::Hash::exists($hash, $mapping->($victim)),
207 my ($keys, $testkeys, $description) = @_;
208 foreach my $key (@$testkeys) {
209 my $lckey = ($key eq chr 198) ? chr 230 : lc $key;
211 utf8::encode $unikey;
213 utf8::downgrade $key, 1;
214 utf8::downgrade $lckey, 1;
215 utf8::downgrade $unikey, 1;
216 main_test_inner ($key, $lckey, $unikey, $keys, $description);
219 utf8::upgrade $lckey;
220 utf8::upgrade $unikey;
221 main_test_inner ($key, $lckey, $unikey, $keys,
222 $description . ' [key utf8 on]');
225 # hv_exists was buggy for tied hashes, in that the raw utf8 key was being
226 # used - the utf8 flag was being lost.
227 perform_test (\&test_absent, (chr 258), $keys, '');
229 perform_test (\&test_fetch_absent, (chr 258), $keys, '');
230 perform_test (\&test_delete_absent, (chr 258), $keys, '');
233 sub main_test_inner {
234 my ($key, $lckey, $unikey, $keys, $description) = @_;
235 perform_test (\&test_present, $key, $keys, $description);
236 perform_test (\&test_fetch_present, $key, $keys, $description);
237 perform_test (\&test_delete_present, $key, $keys, $description);
239 perform_test (\&test_store, $key, $keys, $description, [a=>'cheat']);
240 perform_test (\&test_store, $key, $keys, $description, []);
242 perform_test (\&test_absent, $lckey, $keys, $description);
243 perform_test (\&test_fetch_absent, $lckey, $keys, $description);
244 perform_test (\&test_delete_absent, $lckey, $keys, $description);
246 return if $unikey eq $key;
248 perform_test (\&test_absent, $unikey, $keys, $description);
249 perform_test (\&test_fetch_absent, $unikey, $keys, $description);
250 perform_test (\&test_delete_absent, $unikey, $keys, $description);
254 my ($test_sub, $key, $keys, $message, @other) = @_;
255 my $printable = join ',', map {ord} split //, $key;
257 my (%hash, %tiehash);
258 tie %tiehash, 'Tie::StdHash';
260 @hash{@$keys} = @$keys;
261 @tiehash{@$keys} = @$keys;
263 &$test_sub (\%hash, $key, $printable, $message, @other);
264 &$test_sub (\%tiehash, $key, $printable, "$message tie", @other);
268 my ($hash, $key, $printable, $message) = @_;
270 ok (exists $hash->{$key}, "hv_exists_ent present$message $printable");
271 ok (XS::APItest::Hash::exists ($hash, $key),
272 "hv_exists present$message $printable");
276 my ($hash, $key, $printable, $message) = @_;
278 ok (!exists $hash->{$key}, "hv_exists_ent absent$message $printable");
279 ok (!XS::APItest::Hash::exists ($hash, $key),
280 "hv_exists absent$message $printable");
283 sub test_delete_present {
284 my ($hash, $key, $printable, $message) = @_;
287 my $class = tied %$hash;
288 if (defined $class) {
289 tie %$copy, ref $class;
292 ok (brute_force_exists ($copy, $key),
293 "hv_delete_ent present$message $printable");
294 is (delete $copy->{$key}, $key, "hv_delete_ent present$message $printable");
295 ok (!brute_force_exists ($copy, $key),
296 "hv_delete_ent present$message $printable");
298 ok (brute_force_exists ($copy, $key),
299 "hv_delete present$message $printable");
300 is (XS::APItest::Hash::delete ($copy, $key), $key,
301 "hv_delete present$message $printable");
302 ok (!brute_force_exists ($copy, $key),
303 "hv_delete present$message $printable");
306 sub test_delete_absent {
307 my ($hash, $key, $printable, $message) = @_;
310 my $class = tied %$hash;
311 if (defined $class) {
312 tie %$copy, ref $class;
315 is (delete $copy->{$key}, undef, "hv_delete_ent absent$message $printable");
317 is (XS::APItest::Hash::delete ($copy, $key), undef,
318 "hv_delete absent$message $printable");
322 my ($hash, $key, $printable, $message, $defaults) = @_;
323 my $HV_STORE_IS_CRAZY = 1;
325 # We are cheating - hv_store returns NULL for a store into an empty
326 # tied hash. This isn't helpful here.
328 my $class = tied %$hash;
332 if (defined $class) {
335 $HV_STORE_IS_CRAZY = undef;
337 is (XS::APItest::Hash::store_ent(\%h1, $key, 1), $HV_STORE_IS_CRAZY,
338 "hv_store_ent$message $printable");
339 ok (brute_force_exists (\%h1, $key), "hv_store_ent$message $printable");
340 is (XS::APItest::Hash::store(\%h2, $key, 1), $HV_STORE_IS_CRAZY,
341 "hv_store$message $printable");
342 ok (brute_force_exists (\%h2, $key), "hv_store$message $printable");
345 sub test_fetch_present {
346 my ($hash, $key, $printable, $message) = @_;
348 is ($hash->{$key}, $key, "hv_fetch_ent present$message $printable");
349 is (XS::APItest::Hash::fetch ($hash, $key), $key,
350 "hv_fetch present$message $printable");
353 sub test_fetch_absent {
354 my ($hash, $key, $printable, $message) = @_;
356 is ($hash->{$key}, undef, "hv_fetch_ent absent$message $printable");
357 is (XS::APItest::Hash::fetch ($hash, $key), undef,
358 "hv_fetch absent$message $printable");
361 sub brute_force_exists {
362 my ($hash, $key) = @_;
363 foreach (keys %$hash) {
364 return 1 if $key eq $_;
370 my @results = map {my $a = $_; $a =~ tr/A-Za-z/N-ZA-Mn-za-m/; $a} @_;
371 wantarray ? @results : $results[0];
375 my @results = map {join '', map {chr(32 ^ ord $_)} split '', $_} @_;
376 wantarray ? @results : $results[0];