Integrate:
[p5sagit/p5-mst-13.2.git] / ext / XS / APItest / t / hash.t
CommitLineData
0314122a 1#!perl -w
2
3BEGIN {
4 chdir 't' if -d 't';
5 @INC = '../lib';
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";
12 exit 0;
13 }
14}
15
3128e575 16use strict;
17use utf8;
0314122a 18use Tie::Hash;
3128e575 19use Test::More 'no_plan';
20
55289a74 21BEGIN {use_ok('XS::APItest')};
0314122a 22
3128e575 23sub preform_test;
24sub test_present;
25sub test_absent;
26sub test_delete_present;
27sub test_delete_absent;
28sub brute_force_exists;
29sub test_store;
30sub test_fetch_present;
31sub test_fetch_absent;
0314122a 32
b60cf05a 33my $utf8_for_258 = chr 258;
34utf8::encode $utf8_for_258;
0314122a 35
3128e575 36my @testkeys = ('N', chr 198, chr 256);
b60cf05a 37my @keys = (@testkeys, $utf8_for_258);
0314122a 38
3128e575 39foreach (@keys) {
40 utf8::downgrade $_, 1;
41}
42main_tests (\@keys, \@testkeys, '');
0314122a 43
3128e575 44foreach (@keys) {
45 utf8::upgrade $_;
46}
47main_tests (\@keys, \@testkeys, ' [utf8 hash]');
0314122a 48
3128e575 49{
50 my %h = (a=>'cheat');
51 tie %h, 'Tie::StdHash';
1baaf5d7 52 is (XS::APItest::Hash::store(\%h, chr 258, 1), undef);
3128e575 53
54 ok (!exists $h{$utf8_for_258},
55 "hv_store doesn't insert a key with the raw utf8 on a tied hash");
56}
0314122a 57
5d2b1485 58{
59 my $strtab = strtab();
60 is (ref $strtab, 'HASH', "The shared string table quacks like a hash");
8ca60cef 61 my $wibble = "\0";
5d2b1485 62 eval {
8ca60cef 63 $strtab->{$wibble}++;
5d2b1485 64 };
65 my $prefix = "Cannot modify shared string table in hv_";
66 my $what = $prefix . 'fetch';
67 like ($@, qr/^$what/,$what);
68 eval {
69 XS::APItest::Hash::store($strtab, 'Boom!', 1)
70 };
71 $what = $prefix . 'store';
72 like ($@, qr/^$what/, $what);
73 if (0) {
74 A::B->method();
75 }
76 # DESTROY should be in there.
77 eval {
78 delete $strtab->{DESTROY};
79 };
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
84}
2dc92170 85
86{
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");
91}
35ab5632 92
93foreach my $in ("", "N", "a\0b") {
94 my $got = XS::APItest::Hash::test_share_unshare_pvn($in);
95 is ($got, $in, "test_share_unshare_pvn");
96}
97
55289a74 98if ($] > 5.009) {
53c40a8f 99 foreach ([\&XS::APItest::Hash::rot13_hash, \&rot13, "rot 13"],
100 [\&XS::APItest::Hash::bitflip_hash, \&bitflip, "bitflip"],
101 ) {
102 my ($setup, $mapping, $name) = @$_;
103 my %hash;
104 my %placebo = (a => 1, p => 2, i => 4, e => 8);
105 $setup->(\%hash);
106 $hash{a}++; @hash{qw(p i e)} = (2, 4, 8);
107
108 test_U_hash(\%hash, \%placebo, [f => 9, g => 10, h => 11], $mapping,
109 $name);
110 }
850f5f16 111 foreach my $upgrade_o (0, 1) {
112 foreach my $upgrade_n (0, 1) {
113 my (%hash, %placebo);
114 XS::APItest::Hash::bitflip_hash(\%hash);
115 foreach my $new (["7", 65, 67, 80],
116 ["8", 163, 171, 215],
117 ["U", 2603, 2604, 2604],
118 ) {
119 foreach my $code (78, 240, 256, 1336) {
120 my $key = chr $code;
121 # This is the UTF-8 byte sequence for the key.
122 my $key_utf8 = $key;
123 utf8::encode($key_utf8);
124 if ($upgrade_o) {
125 $key .= chr 256;
126 chop $key;
127 }
128 $hash{$key} = $placebo{$key} = $code;
129 $hash{$key_utf8} = $placebo{$key_utf8} = "$code as UTF-8";
130 }
131 my $name = 'bitflip ' . shift @$new;
132 my @new_kv;
133 foreach my $code (@$new) {
134 my $key = chr $code;
135 if ($upgrade_n) {
136 $key .= chr 256;
137 chop $key;
138 }
139 push @new_kv, $key, $_;
140 }
141
142 $name .= ' upgraded(orig) ' if $upgrade_o;
143 $name .= ' upgraded(new) ' if $upgrade_n;
144 test_U_hash(\%hash, \%placebo, \@new_kv, \&bitflip, $name);
145 }
146 }
147 }
53c40a8f 148}
149
150exit;
151
152################################ The End ################################
153
154sub test_U_hash {
155 my ($hash, $placebo, $new, $mapping, $message) = @_;
156 my @hitlist = keys %$placebo;
157 print "# $message\n";
b54b4831 158
53c40a8f 159 my @keys = sort keys %$hash;
160 is ("@keys", join(' ', sort($mapping->(keys %$placebo))),
161 "uvar magic called exactly once on store");
b54b4831 162
850f5f16 163 is (keys %$hash, keys %$placebo);
55289a74 164
53c40a8f 165 my $victim = shift @hitlist;
166 is (delete $hash->{$victim}, delete $placebo->{$victim});
55289a74 167
850f5f16 168 is (keys %$hash, keys %$placebo);
53c40a8f 169 @keys = sort keys %$hash;
170 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
55289a74 171
53c40a8f 172 $victim = shift @hitlist;
173 is (XS::APItest::Hash::delete_ent ($hash, $victim,
55289a74 174 XS::APItest::HV_DISABLE_UVAR_XKEY),
175 undef, "Deleting a known key with conversion disabled fails (ent)");
850f5f16 176 is (keys %$hash, keys %$placebo);
55289a74 177
53c40a8f 178 is (XS::APItest::Hash::delete_ent ($hash, $victim, 0),
179 delete $placebo->{$victim},
180 "Deleting a known key with conversion enabled works (ent)");
850f5f16 181 is (keys %$hash, keys %$placebo);
53c40a8f 182 @keys = sort keys %$hash;
183 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
55289a74 184
53c40a8f 185 $victim = shift @hitlist;
186 is (XS::APItest::Hash::delete ($hash, $victim,
55289a74 187 XS::APItest::HV_DISABLE_UVAR_XKEY),
188 undef, "Deleting a known key with conversion disabled fails");
850f5f16 189 is (keys %$hash, keys %$placebo);
53c40a8f 190
191 is (XS::APItest::Hash::delete ($hash, $victim, 0),
192 delete $placebo->{$victim},
193 "Deleting a known key with conversion enabled works");
850f5f16 194 is (keys %$hash, keys %$placebo);
53c40a8f 195 @keys = sort keys %$hash;
196 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
197
198 my ($k, $v) = splice @$new, 0, 2;
199 $hash->{$k} = $v;
200 $placebo->{$k} = $v;
850f5f16 201 is (keys %$hash, keys %$placebo);
53c40a8f 202 @keys = sort keys %$hash;
203 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
204
205 ($k, $v) = splice @$new, 0, 2;
206 is (XS::APItest::Hash::store_ent($hash, $k, $v), $v, "store_ent");
207 $placebo->{$k} = $v;
850f5f16 208 is (keys %$hash, keys %$placebo);
53c40a8f 209 @keys = sort keys %$hash;
210 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
211
212 ($k, $v) = splice @$new, 0, 2;
213 is (XS::APItest::Hash::store($hash, $k, $v), $v, "store");
53c40a8f 214 $placebo->{$k} = $v;
850f5f16 215 is (keys %$hash, keys %$placebo);
53c40a8f 216 @keys = sort keys %$hash;
217 is ("@keys", join(' ', sort($mapping->(keys %$placebo))));
218
219 @hitlist = keys %$placebo;
220 $victim = shift @hitlist;
221 is (XS::APItest::Hash::fetch_ent($hash, $victim), $placebo->{$victim},
222 "fetch_ent");
223 is (XS::APItest::Hash::fetch_ent($hash, $mapping->($victim)), undef,
bdee33e4 224 "fetch_ent (missing)");
225
53c40a8f 226 $victim = shift @hitlist;
227 is (XS::APItest::Hash::fetch($hash, $victim), $placebo->{$victim},
228 "fetch");
229 is (XS::APItest::Hash::fetch($hash, $mapping->($victim)), undef,
bdee33e4 230 "fetch (missing)");
231
53c40a8f 232 $victim = shift @hitlist;
233 ok (XS::APItest::Hash::exists_ent($hash, $victim), "exists_ent");
234 ok (!XS::APItest::Hash::exists_ent($hash, $mapping->($victim)),
bdee33e4 235 "exists_ent (missing)");
236
53c40a8f 237 $victim = shift @hitlist;
6b4de907 238 die "Need a victim" unless defined $victim;
53c40a8f 239 ok (XS::APItest::Hash::exists($hash, $victim), "exists");
240 ok (!XS::APItest::Hash::exists($hash, $mapping->($victim)),
241 "exists (missing)");
6b4de907 242
243 is (XS::APItest::Hash::common({hv => $hash, keysv => $victim}),
244 $placebo->{$victim}, "common (fetch)");
245 is (XS::APItest::Hash::common({hv => $hash, keypv => $victim}),
246 $placebo->{$victim}, "common (fetch pv)");
247 is (XS::APItest::Hash::common({hv => $hash, keysv => $victim,
248 action => XS::APItest::HV_DISABLE_UVAR_XKEY}),
249 undef, "common (fetch) missing");
250 is (XS::APItest::Hash::common({hv => $hash, keypv => $victim,
251 action => XS::APItest::HV_DISABLE_UVAR_XKEY}),
252 undef, "common (fetch pv) missing");
253 is (XS::APItest::Hash::common({hv => $hash, keysv => $mapping->($victim),
254 action => XS::APItest::HV_DISABLE_UVAR_XKEY}),
255 $placebo->{$victim}, "common (fetch) missing mapped");
256 is (XS::APItest::Hash::common({hv => $hash, keypv => $mapping->($victim),
257 action => XS::APItest::HV_DISABLE_UVAR_XKEY}),
258 $placebo->{$victim}, "common (fetch pv) missing mapped");
b54b4831 259}
260
3128e575 261sub main_tests {
262 my ($keys, $testkeys, $description) = @_;
263 foreach my $key (@$testkeys) {
264 my $lckey = ($key eq chr 198) ? chr 230 : lc $key;
265 my $unikey = $key;
266 utf8::encode $unikey;
0314122a 267
3128e575 268 utf8::downgrade $key, 1;
269 utf8::downgrade $lckey, 1;
270 utf8::downgrade $unikey, 1;
271 main_test_inner ($key, $lckey, $unikey, $keys, $description);
0314122a 272
3128e575 273 utf8::upgrade $key;
274 utf8::upgrade $lckey;
275 utf8::upgrade $unikey;
276 main_test_inner ($key, $lckey, $unikey, $keys,
277 $description . ' [key utf8 on]');
278 }
0314122a 279
3128e575 280 # hv_exists was buggy for tied hashes, in that the raw utf8 key was being
281 # used - the utf8 flag was being lost.
282 perform_test (\&test_absent, (chr 258), $keys, '');
0314122a 283
3128e575 284 perform_test (\&test_fetch_absent, (chr 258), $keys, '');
285 perform_test (\&test_delete_absent, (chr 258), $keys, '');
0314122a 286}
287
3128e575 288sub main_test_inner {
289 my ($key, $lckey, $unikey, $keys, $description) = @_;
290 perform_test (\&test_present, $key, $keys, $description);
291 perform_test (\&test_fetch_present, $key, $keys, $description);
292 perform_test (\&test_delete_present, $key, $keys, $description);
b60cf05a 293
3128e575 294 perform_test (\&test_store, $key, $keys, $description, [a=>'cheat']);
295 perform_test (\&test_store, $key, $keys, $description, []);
b60cf05a 296
3128e575 297 perform_test (\&test_absent, $lckey, $keys, $description);
298 perform_test (\&test_fetch_absent, $lckey, $keys, $description);
299 perform_test (\&test_delete_absent, $lckey, $keys, $description);
b60cf05a 300
3128e575 301 return if $unikey eq $key;
302
303 perform_test (\&test_absent, $unikey, $keys, $description);
304 perform_test (\&test_fetch_absent, $unikey, $keys, $description);
305 perform_test (\&test_delete_absent, $unikey, $keys, $description);
b60cf05a 306}
307
3128e575 308sub perform_test {
309 my ($test_sub, $key, $keys, $message, @other) = @_;
b60cf05a 310 my $printable = join ',', map {ord} split //, $key;
311
3128e575 312 my (%hash, %tiehash);
313 tie %tiehash, 'Tie::StdHash';
b60cf05a 314
3128e575 315 @hash{@$keys} = @$keys;
316 @tiehash{@$keys} = @$keys;
b60cf05a 317
3128e575 318 &$test_sub (\%hash, $key, $printable, $message, @other);
319 &$test_sub (\%tiehash, $key, $printable, "$message tie", @other);
b60cf05a 320}
321
3128e575 322sub test_present {
323 my ($hash, $key, $printable, $message) = @_;
324
325 ok (exists $hash->{$key}, "hv_exists_ent present$message $printable");
326 ok (XS::APItest::Hash::exists ($hash, $key),
327 "hv_exists present$message $printable");
b60cf05a 328}
329
3128e575 330sub test_absent {
331 my ($hash, $key, $printable, $message) = @_;
858117f8 332
3128e575 333 ok (!exists $hash->{$key}, "hv_exists_ent absent$message $printable");
334 ok (!XS::APItest::Hash::exists ($hash, $key),
335 "hv_exists absent$message $printable");
b60cf05a 336}
337
3128e575 338sub test_delete_present {
339 my ($hash, $key, $printable, $message) = @_;
b60cf05a 340
3128e575 341 my $copy = {};
342 my $class = tied %$hash;
343 if (defined $class) {
344 tie %$copy, ref $class;
345 }
346 $copy = {%$hash};
8829b5e2 347 ok (brute_force_exists ($copy, $key),
348 "hv_delete_ent present$message $printable");
3128e575 349 is (delete $copy->{$key}, $key, "hv_delete_ent present$message $printable");
8829b5e2 350 ok (!brute_force_exists ($copy, $key),
351 "hv_delete_ent present$message $printable");
3128e575 352 $copy = {%$hash};
8829b5e2 353 ok (brute_force_exists ($copy, $key),
354 "hv_delete present$message $printable");
3128e575 355 is (XS::APItest::Hash::delete ($copy, $key), $key,
356 "hv_delete present$message $printable");
8829b5e2 357 ok (!brute_force_exists ($copy, $key),
358 "hv_delete present$message $printable");
b60cf05a 359}
360
3128e575 361sub test_delete_absent {
362 my ($hash, $key, $printable, $message) = @_;
b60cf05a 363
3128e575 364 my $copy = {};
365 my $class = tied %$hash;
366 if (defined $class) {
367 tie %$copy, ref $class;
368 }
369 $copy = {%$hash};
370 is (delete $copy->{$key}, undef, "hv_delete_ent absent$message $printable");
371 $copy = {%$hash};
372 is (XS::APItest::Hash::delete ($copy, $key), undef,
373 "hv_delete absent$message $printable");
b60cf05a 374}
375
3128e575 376sub test_store {
377 my ($hash, $key, $printable, $message, $defaults) = @_;
378 my $HV_STORE_IS_CRAZY = 1;
b60cf05a 379
3128e575 380 # We are cheating - hv_store returns NULL for a store into an empty
381 # tied hash. This isn't helpful here.
0314122a 382
3128e575 383 my $class = tied %$hash;
0314122a 384
3128e575 385 my %h1 = @$defaults;
386 my %h2 = @$defaults;
387 if (defined $class) {
388 tie %h1, ref $class;
389 tie %h2, ref $class;
1baaf5d7 390 $HV_STORE_IS_CRAZY = undef;
3128e575 391 }
1baaf5d7 392 is (XS::APItest::Hash::store_ent(\%h1, $key, 1), $HV_STORE_IS_CRAZY,
3128e575 393 "hv_store_ent$message $printable");
394 ok (brute_force_exists (\%h1, $key), "hv_store_ent$message $printable");
395 is (XS::APItest::Hash::store(\%h2, $key, 1), $HV_STORE_IS_CRAZY,
396 "hv_store$message $printable");
397 ok (brute_force_exists (\%h2, $key), "hv_store$message $printable");
398}
0314122a 399
3128e575 400sub test_fetch_present {
401 my ($hash, $key, $printable, $message) = @_;
b60cf05a 402
3128e575 403 is ($hash->{$key}, $key, "hv_fetch_ent present$message $printable");
404 is (XS::APItest::Hash::fetch ($hash, $key), $key,
405 "hv_fetch present$message $printable");
0314122a 406}
407
3128e575 408sub test_fetch_absent {
409 my ($hash, $key, $printable, $message) = @_;
b60cf05a 410
3128e575 411 is ($hash->{$key}, undef, "hv_fetch_ent absent$message $printable");
412 is (XS::APItest::Hash::fetch ($hash, $key), undef,
413 "hv_fetch absent$message $printable");
414}
b60cf05a 415
3128e575 416sub brute_force_exists {
417 my ($hash, $key) = @_;
418 foreach (keys %$hash) {
419 return 1 if $key eq $_;
420 }
421 return 0;
b60cf05a 422}
b54b4831 423
424sub rot13 {
425 my @results = map {my $a = $_; $a =~ tr/A-Za-z/N-ZA-Mn-za-m/; $a} @_;
426 wantarray ? @results : $results[0];
427}
53c40a8f 428
429sub bitflip {
430 my @results = map {join '', map {chr(32 ^ ord $_)} split '', $_} @_;
431 wantarray ? @results : $results[0];
432}