xref: /openbsd-src/gnu/usr.bin/perl/ext/Hash-Util/t/Util.t (revision eac174f2741a08d8deb8aae59a7f778ef9b5d770)
143003dfeSmillert#!/usr/bin/perl -Tw
243003dfeSmillert
343003dfeSmillertBEGIN {
443003dfeSmillert    if ($ENV{PERL_CORE}) {
543003dfeSmillert	require Config; import Config;
6b39c5158Smillert	no warnings 'once';
743003dfeSmillert	if ($Config{extensions} !~ /\bHash\/Util\b/) {
843003dfeSmillert	    print "1..0 # Skip: Hash::Util was not built\n";
943003dfeSmillert	    exit 0;
1043003dfeSmillert	}
1143003dfeSmillert    }
1243003dfeSmillert}
1343003dfeSmillert
1443003dfeSmillertuse strict;
1543003dfeSmillertuse Test::More;
16b8851fccSafresh1
17b8851fccSafresh1sub numbers_first { # Sort helper: All digit entries sort in front of others
18b8851fccSafresh1                    # Makes sorting portable across ASCII/EBCDIC
19b8851fccSafresh1    return $a cmp $b if ($a =~ /^\d+$/) == ($b =~ /^\d+$/);
20b8851fccSafresh1    return -1 if $a =~ /^\d+$/;
21b8851fccSafresh1    return 1;
22b8851fccSafresh1}
23b8851fccSafresh1
2443003dfeSmillertmy @Exported_Funcs;
2543003dfeSmillertBEGIN {
2643003dfeSmillert    @Exported_Funcs = qw(
2791f110e0Safresh1                     fieldhash fieldhashes
2891f110e0Safresh1
2991f110e0Safresh1                     all_keys
3043003dfeSmillert                     lock_keys unlock_keys
3143003dfeSmillert                     lock_value unlock_value
3243003dfeSmillert                     lock_hash unlock_hash
3391f110e0Safresh1                     lock_keys_plus
3491f110e0Safresh1                     hash_locked hash_unlocked
3591f110e0Safresh1                     hashref_locked hashref_unlocked
3643003dfeSmillert                     hidden_keys legal_keys
3743003dfeSmillert
3843003dfeSmillert                     lock_ref_keys unlock_ref_keys
3943003dfeSmillert                     lock_ref_value unlock_ref_value
4043003dfeSmillert                     lock_hashref unlock_hashref
4191f110e0Safresh1                     lock_ref_keys_plus
4243003dfeSmillert                     hidden_ref_keys legal_ref_keys
4343003dfeSmillert
4491f110e0Safresh1                     hash_seed hash_value bucket_stats bucket_info bucket_array
4591f110e0Safresh1                     hv_store
4691f110e0Safresh1                     lock_hash_recurse unlock_hash_recurse
47b8851fccSafresh1                     lock_hashref_recurse unlock_hashref_recurse
4843003dfeSmillert                    );
49*eac174f2Safresh1    plan tests => 250 + @Exported_Funcs;
5043003dfeSmillert    use_ok 'Hash::Util', @Exported_Funcs;
5143003dfeSmillert}
5243003dfeSmillertforeach my $func (@Exported_Funcs) {
5343003dfeSmillert    can_ok __PACKAGE__, $func;
5443003dfeSmillert}
5543003dfeSmillert
5643003dfeSmillertmy %hash = (foo => 42, bar => 23, locked => 'yep');
5743003dfeSmillertlock_keys(%hash);
5843003dfeSmillerteval { $hash{baz} = 99; };
5943003dfeSmillertlike( $@, qr/^Attempt to access disallowed key 'baz' in a restricted hash/,
6043003dfeSmillert                                                       'lock_keys()');
6191f110e0Safresh1is( $hash{bar}, 23, '$hash{bar} == 23' );
6243003dfeSmillertok( !exists $hash{baz},'!exists $hash{baz}' );
6343003dfeSmillert
6443003dfeSmillertdelete $hash{bar};
6543003dfeSmillertok( !exists $hash{bar},'!exists $hash{bar}' );
6643003dfeSmillert$hash{bar} = 69;
6743003dfeSmillertis( $hash{bar}, 69 ,'$hash{bar} == 69');
6843003dfeSmillert
6943003dfeSmillerteval { () = $hash{i_dont_exist} };
7043003dfeSmillertlike( $@, qr/^Attempt to access disallowed key 'i_dont_exist' in a restricted hash/,
7143003dfeSmillert      'Disallowed 1' );
7243003dfeSmillert
7343003dfeSmillertlock_value(%hash, 'locked');
7443003dfeSmillerteval { print "# oops" if $hash{four} };
7543003dfeSmillertlike( $@, qr/^Attempt to access disallowed key 'four' in a restricted hash/,
7643003dfeSmillert      'Disallowed 2' );
7743003dfeSmillert
7843003dfeSmillerteval { $hash{"\x{2323}"} = 3 };
7943003dfeSmillertlike( $@, qr/^Attempt to access disallowed key '(.*)' in a restricted hash/,
8043003dfeSmillert                                               'wide hex key' );
8143003dfeSmillert
8243003dfeSmillerteval { delete $hash{locked} };
8343003dfeSmillertlike( $@, qr/^Attempt to delete readonly key 'locked' from a restricted hash/,
8443003dfeSmillert                                           'trying to delete a locked key' );
8543003dfeSmillerteval { $hash{locked} = 42; };
8643003dfeSmillertlike( $@, qr/^Modification of a read-only value attempted/,
8743003dfeSmillert                                           'trying to change a locked key' );
8891f110e0Safresh1is( $hash{locked}, 'yep', '$hash{locked} is yep' );
8943003dfeSmillert
9043003dfeSmillerteval { delete $hash{I_dont_exist} };
9143003dfeSmillertlike( $@, qr/^Attempt to delete disallowed key 'I_dont_exist' from a restricted hash/,
9243003dfeSmillert                             'trying to delete a key that doesnt exist' );
9343003dfeSmillert
9443003dfeSmillertok( !exists $hash{I_dont_exist},'!exists $hash{I_dont_exist}' );
9543003dfeSmillert
9643003dfeSmillertunlock_keys(%hash);
9743003dfeSmillert$hash{I_dont_exist} = 42;
9843003dfeSmillertis( $hash{I_dont_exist}, 42,    'unlock_keys' );
9943003dfeSmillert
10043003dfeSmillerteval { $hash{locked} = 42; };
10143003dfeSmillertlike( $@, qr/^Modification of a read-only value attempted/,
10243003dfeSmillert                             '  individual key still readonly' );
10343003dfeSmillerteval { delete $hash{locked} },
10443003dfeSmillertis( $@, '', '  but can be deleted :(' );
10543003dfeSmillert
10643003dfeSmillertunlock_value(%hash, 'locked');
10743003dfeSmillert$hash{locked} = 42;
10843003dfeSmillertis( $hash{locked}, 42,  'unlock_value' );
10943003dfeSmillert
11043003dfeSmillert
11143003dfeSmillert{
11243003dfeSmillert    my %hash = ( foo => 42, locked => 23 );
11343003dfeSmillert
11443003dfeSmillert    lock_keys(%hash);
11543003dfeSmillert    eval { %hash = ( wubble => 42 ) };  # we know this will bomb
11643003dfeSmillert    like( $@, qr/^Attempt to access disallowed key 'wubble'/,'Disallowed 3' );
11743003dfeSmillert    unlock_keys(%hash);
11843003dfeSmillert}
11943003dfeSmillert
12043003dfeSmillert{
12143003dfeSmillert    my %hash = (KEY => 'val', RO => 'val');
12243003dfeSmillert    lock_keys(%hash);
12343003dfeSmillert    lock_value(%hash, 'RO');
12443003dfeSmillert
12543003dfeSmillert    eval { %hash = (KEY => 1) };
12691f110e0Safresh1    like( $@, qr/^Attempt to delete readonly key 'RO' from a restricted hash/,
12791f110e0Safresh1        'attempt to delete readonly key from restricted hash' );
12843003dfeSmillert}
12943003dfeSmillert
13043003dfeSmillert{
13143003dfeSmillert    my %hash = (KEY => 1, RO => 2);
13243003dfeSmillert    lock_keys(%hash);
13343003dfeSmillert    eval { %hash = (KEY => 1, RO => 2) };
13491f110e0Safresh1    is( $@, '', 'No error message, as expected');
13543003dfeSmillert}
13643003dfeSmillert
13743003dfeSmillert{
13843003dfeSmillert    my %hash = ();
13943003dfeSmillert    lock_keys(%hash, qw(foo bar));
14043003dfeSmillert    is( keys %hash, 0,  'lock_keys() w/keyset shouldnt add new keys' );
14143003dfeSmillert    $hash{foo} = 42;
14291f110e0Safresh1    is( keys %hash, 1, '1 element in hash' );
14343003dfeSmillert    eval { $hash{wibble} = 42 };
14443003dfeSmillert    like( $@, qr/^Attempt to access disallowed key 'wibble' in a restricted hash/,
14543003dfeSmillert                        'write threw error (locked)');
14643003dfeSmillert
14743003dfeSmillert    unlock_keys(%hash);
14843003dfeSmillert    eval { $hash{wibble} = 23; };
14943003dfeSmillert    is( $@, '', 'unlock_keys' );
15043003dfeSmillert}
15143003dfeSmillert
15243003dfeSmillert{
15343003dfeSmillert    my %hash = (foo => 42, bar => undef, baz => 0);
15443003dfeSmillert    lock_keys(%hash, qw(foo bar baz up down));
15543003dfeSmillert    is( keys %hash, 3,   'lock_keys() w/keyset didnt add new keys' );
15643003dfeSmillert    is_deeply( \%hash, { foo => 42, bar => undef, baz => 0 },'is_deeply' );
15743003dfeSmillert
15843003dfeSmillert    eval { $hash{up} = 42; };
15943003dfeSmillert    is( $@, '','No error 1' );
16043003dfeSmillert
16143003dfeSmillert    eval { $hash{wibble} = 23 };
16243003dfeSmillert    like( $@, qr/^Attempt to access disallowed key 'wibble' in a restricted hash/,
16343003dfeSmillert          'locked "wibble"' );
16443003dfeSmillert}
16543003dfeSmillert
16643003dfeSmillert{
16743003dfeSmillert    my %hash = (foo => 42, bar => undef);
16843003dfeSmillert    eval { lock_keys(%hash, qw(foo baz)); };
16991f110e0Safresh1    like( $@, qr/^Hash has key 'bar' which is not in the new key set/,
17043003dfeSmillert                    'carp test' );
17143003dfeSmillert}
17243003dfeSmillert
17343003dfeSmillert{
17443003dfeSmillert    my %hash = (foo => 42, bar => 23);
17543003dfeSmillert    lock_hash( %hash );
17691f110e0Safresh1    ok( hashref_locked( \%hash ), 'hashref_locked' );
17791f110e0Safresh1    ok( hash_locked( %hash ), 'hash_locked' );
17843003dfeSmillert
17943003dfeSmillert    ok( Internals::SvREADONLY(%hash),'Was locked %hash' );
18043003dfeSmillert    ok( Internals::SvREADONLY($hash{foo}),'Was locked $hash{foo}' );
18143003dfeSmillert    ok( Internals::SvREADONLY($hash{bar}),'Was locked $hash{bar}' );
18243003dfeSmillert
18343003dfeSmillert    unlock_hash ( %hash );
18491f110e0Safresh1    ok( hashref_unlocked( { %hash } ), 'hashref_unlocked' );
18591f110e0Safresh1    ok( hash_unlocked( %hash ), 'hash_unlocked' );
18643003dfeSmillert
18743003dfeSmillert    ok( !Internals::SvREADONLY(%hash),'Was unlocked %hash' );
18843003dfeSmillert    ok( !Internals::SvREADONLY($hash{foo}),'Was unlocked $hash{foo}' );
18943003dfeSmillert    ok( !Internals::SvREADONLY($hash{bar}),'Was unlocked $hash{bar}' );
19043003dfeSmillert}
19143003dfeSmillert
19291f110e0Safresh1{
19391f110e0Safresh1    my %hash = (foo => 42, bar => 23);
19491f110e0Safresh1    ok( ! hashref_locked( { %hash } ), 'hashref_locked negated' );
19591f110e0Safresh1    ok( ! hash_locked( %hash ), 'hash_locked negated' );
19691f110e0Safresh1
19791f110e0Safresh1    lock_hash( %hash );
19891f110e0Safresh1    ok( ! hashref_unlocked( \%hash ), 'hashref_unlocked negated' );
19991f110e0Safresh1    ok( ! hash_unlocked( %hash ), 'hash_unlocked negated' );
20091f110e0Safresh1}
20143003dfeSmillert
20243003dfeSmillertlock_keys(%ENV);
20343003dfeSmillerteval { () = $ENV{I_DONT_EXIST} };
20491f110e0Safresh1like(
20591f110e0Safresh1    $@,
20691f110e0Safresh1    qr/^Attempt to access disallowed key 'I_DONT_EXIST' in a restricted hash/,
20791f110e0Safresh1    'locked %ENV'
20891f110e0Safresh1);
2096fb12b70Safresh1unlock_keys(%ENV); # Test::Builder cannot print test failures otherwise
21043003dfeSmillert
21143003dfeSmillert{
21243003dfeSmillert    my %hash;
21343003dfeSmillert
21443003dfeSmillert    lock_keys(%hash, 'first');
21543003dfeSmillert
21643003dfeSmillert    is (scalar keys %hash, 0, "place holder isn't a key");
21743003dfeSmillert    $hash{first} = 1;
21843003dfeSmillert    is (scalar keys %hash, 1, "we now have a key");
21943003dfeSmillert    delete $hash{first};
22043003dfeSmillert    is (scalar keys %hash, 0, "now no key");
22143003dfeSmillert
22243003dfeSmillert    unlock_keys(%hash);
22343003dfeSmillert
22443003dfeSmillert    $hash{interregnum} = 1.5;
22543003dfeSmillert    is (scalar keys %hash, 1, "key again");
22643003dfeSmillert    delete $hash{interregnum};
22743003dfeSmillert    is (scalar keys %hash, 0, "no key again");
22843003dfeSmillert
22943003dfeSmillert    lock_keys(%hash, 'second');
23043003dfeSmillert
23143003dfeSmillert    is (scalar keys %hash, 0, "place holder isn't a key");
23243003dfeSmillert
23343003dfeSmillert    eval {$hash{zeroeth} = 0};
23443003dfeSmillert    like ($@,
23543003dfeSmillert          qr/^Attempt to access disallowed key 'zeroeth' in a restricted hash/,
23643003dfeSmillert          'locked key never mentioned before should fail');
23743003dfeSmillert    eval {$hash{first} = -1};
23843003dfeSmillert    like ($@,
23943003dfeSmillert          qr/^Attempt to access disallowed key 'first' in a restricted hash/,
24043003dfeSmillert          'previously locked place holders should also fail');
24143003dfeSmillert    is (scalar keys %hash, 0, "and therefore there are no keys");
24243003dfeSmillert    $hash{second} = 1;
24343003dfeSmillert    is (scalar keys %hash, 1, "we now have just one key");
24443003dfeSmillert    delete $hash{second};
24543003dfeSmillert    is (scalar keys %hash, 0, "back to zero");
24643003dfeSmillert
24743003dfeSmillert    unlock_keys(%hash); # We have deliberately left a placeholder.
24843003dfeSmillert
24943003dfeSmillert    $hash{void} = undef;
25043003dfeSmillert    $hash{nowt} = undef;
25143003dfeSmillert
25243003dfeSmillert    is (scalar keys %hash, 2, "two keys, values both undef");
25343003dfeSmillert
25443003dfeSmillert    lock_keys(%hash);
25543003dfeSmillert
25643003dfeSmillert    is (scalar keys %hash, 2, "still two keys after locking");
25743003dfeSmillert
25843003dfeSmillert    eval {$hash{second} = -1};
25943003dfeSmillert    like ($@,
26043003dfeSmillert          qr/^Attempt to access disallowed key 'second' in a restricted hash/,
26143003dfeSmillert          'previously locked place holders should fail');
26243003dfeSmillert
26343003dfeSmillert    is ($hash{void}, undef,
26443003dfeSmillert        "undef values should not be misunderstood as placeholders");
26543003dfeSmillert    is ($hash{nowt}, undef,
26643003dfeSmillert        "undef values should not be misunderstood as placeholders (again)");
26743003dfeSmillert}
26843003dfeSmillert
26943003dfeSmillert{
27043003dfeSmillert  # perl #18651 - tim@consultix-inc.com found a rather nasty data dependant
27143003dfeSmillert  # bug whereby hash iterators could lose hash keys (and values, as the code
27243003dfeSmillert  # is common) for restricted hashes.
27343003dfeSmillert
27443003dfeSmillert  my @keys = qw(small medium large);
27543003dfeSmillert
27643003dfeSmillert  # There should be no difference whether it is restricted or not
27743003dfeSmillert  foreach my $lock (0, 1) {
27843003dfeSmillert    # Try setting all combinations of the 3 keys
27943003dfeSmillert    foreach my $usekeys (0..7) {
28043003dfeSmillert      my @usekeys;
28143003dfeSmillert      for my $bits (0,1,2) {
28243003dfeSmillert	push @usekeys, $keys[$bits] if $usekeys & (1 << $bits);
28343003dfeSmillert      }
28443003dfeSmillert      my %clean = map {$_ => length $_} @usekeys;
28543003dfeSmillert      my %target;
28643003dfeSmillert      lock_keys ( %target, @keys ) if $lock;
28743003dfeSmillert
28843003dfeSmillert      while (my ($k, $v) = each %clean) {
28943003dfeSmillert	$target{$k} = $v;
29043003dfeSmillert      }
29143003dfeSmillert
29243003dfeSmillert      my $message
29343003dfeSmillert	= ($lock ? 'locked' : 'not locked') . ' keys ' . join ',', @usekeys;
29443003dfeSmillert
29543003dfeSmillert      is (scalar keys %target, scalar keys %clean, "scalar keys for $message");
29643003dfeSmillert      is (scalar values %target, scalar values %clean,
29743003dfeSmillert	  "scalar values for $message");
29843003dfeSmillert      # Yes. All these sorts are necessary. Even for "identical hashes"
29943003dfeSmillert      # Because the data dependency of the test involves two of the strings
30043003dfeSmillert      # colliding on the same bucket, so the iterator order (output of keys,
30143003dfeSmillert      # values, each) depends on the addition order in the hash. And locking
30243003dfeSmillert      # the keys of the hash involves behind the scenes key additions.
30343003dfeSmillert      is_deeply( [sort keys %target] , [sort keys %clean],
30443003dfeSmillert		 "list keys for $message");
30543003dfeSmillert      is_deeply( [sort values %target] , [sort values %clean],
30643003dfeSmillert		 "list values for $message");
30743003dfeSmillert
30843003dfeSmillert      is_deeply( [sort %target] , [sort %clean],
30943003dfeSmillert		 "hash in list context for $message");
31043003dfeSmillert
31143003dfeSmillert      my (@clean, @target);
31243003dfeSmillert      while (my ($k, $v) = each %clean) {
31343003dfeSmillert	push @clean, $k, $v;
31443003dfeSmillert      }
31543003dfeSmillert      while (my ($k, $v) = each %target) {
31643003dfeSmillert	push @target, $k, $v;
31743003dfeSmillert      }
31843003dfeSmillert
31943003dfeSmillert      is_deeply( [sort @target] , [sort @clean],
32043003dfeSmillert		 "iterating with each for $message");
32143003dfeSmillert    }
32243003dfeSmillert  }
32343003dfeSmillert}
32443003dfeSmillert
32543003dfeSmillert# Check clear works on locked empty hashes - SEGVs on 5.8.2.
32643003dfeSmillert{
32743003dfeSmillert    my %hash;
32843003dfeSmillert    lock_hash(%hash);
32943003dfeSmillert    %hash = ();
33043003dfeSmillert    ok(keys(%hash) == 0, 'clear empty lock_hash() hash');
33143003dfeSmillert}
33243003dfeSmillert{
33343003dfeSmillert    my %hash;
33443003dfeSmillert    lock_keys(%hash);
33543003dfeSmillert    %hash = ();
33643003dfeSmillert    ok(keys(%hash) == 0, 'clear empty lock_keys() hash');
33743003dfeSmillert}
33843003dfeSmillert
3396fb12b70Safresh1# Copy-on-write scalars should not be deletable after lock_hash;
3406fb12b70Safresh1{
3416fb12b70Safresh1    my %hash = (key=>__PACKAGE__);
3426fb12b70Safresh1    lock_hash(%hash);
3436fb12b70Safresh1    eval { delete $hash{key} };
3446fb12b70Safresh1    like $@, qr/^Attempt to delete readonly key /,
3456fb12b70Safresh1        'COW scalars are not exempt from lock_hash (delete)';
3466fb12b70Safresh1    eval { %hash = () };
3476fb12b70Safresh1    like $@, qr/^Attempt to delete readonly key /,
3486fb12b70Safresh1        'COW scalars are not exempt from lock_hash (clear)';
3496fb12b70Safresh1}
3506fb12b70Safresh1
35143003dfeSmillertmy $hash_seed = hash_seed();
35291f110e0Safresh1ok(defined($hash_seed) && $hash_seed ne '', "hash_seed $hash_seed");
35343003dfeSmillert
35443003dfeSmillert{
35543003dfeSmillert    package Minder;
35643003dfeSmillert    my $counter;
35743003dfeSmillert    sub DESTROY {
35843003dfeSmillert	--$counter;
35943003dfeSmillert    }
36043003dfeSmillert    sub new {
36143003dfeSmillert	++$counter;
36243003dfeSmillert	bless [], __PACKAGE__;
36343003dfeSmillert    }
36443003dfeSmillert    package main;
36543003dfeSmillert
36643003dfeSmillert    for my $state ('', 'locked') {
36743003dfeSmillert	my $a = Minder->new();
36843003dfeSmillert	is ($counter, 1, "There is 1 object $state");
36943003dfeSmillert	my %hash;
37043003dfeSmillert	$hash{a} = $a;
37143003dfeSmillert	is ($counter, 1, "There is still 1 object $state");
37243003dfeSmillert
37343003dfeSmillert	lock_keys(%hash) if $state;
37443003dfeSmillert
37543003dfeSmillert	is ($counter, 1, "There is still 1 object $state");
37643003dfeSmillert	undef $a;
37743003dfeSmillert	is ($counter, 1, "Still 1 object $state");
37843003dfeSmillert	delete $hash{a};
37943003dfeSmillert	is ($counter, 0, "0 objects when hash key is deleted $state");
38043003dfeSmillert	$hash{a} = undef;
38143003dfeSmillert	is ($counter, 0, "Still 0 objects $state");
38243003dfeSmillert	%hash = ();
38343003dfeSmillert	is ($counter, 0, "0 objects after clear $state");
38443003dfeSmillert    }
38543003dfeSmillert}
38643003dfeSmillert{
38743003dfeSmillert    my %hash = map {$_,$_} qw(fwiffffff foosht teeoo);
38843003dfeSmillert    lock_keys(%hash);
38943003dfeSmillert    delete $hash{fwiffffff};
39043003dfeSmillert    is (scalar keys %hash, 2,"Count of keys after delete on locked hash");
39143003dfeSmillert    unlock_keys(%hash);
39243003dfeSmillert    is (scalar keys %hash, 2,"Count of keys after unlock");
39343003dfeSmillert
39443003dfeSmillert    my ($first, $value) = each %hash;
39543003dfeSmillert    is ($hash{$first}, $value, "Key has the expected value before the lock");
39643003dfeSmillert    lock_keys(%hash);
39743003dfeSmillert    is ($hash{$first}, $value, "Key has the expected value after the lock");
39843003dfeSmillert
39943003dfeSmillert    my ($second, $v2) = each %hash;
40043003dfeSmillert
40143003dfeSmillert    is ($hash{$first}, $value, "Still correct after iterator advances");
40243003dfeSmillert    is ($hash{$second}, $v2, "Other key has the expected value");
40343003dfeSmillert}
40443003dfeSmillert{
40543003dfeSmillert    my $x='foo';
40643003dfeSmillert    my %test;
40743003dfeSmillert    hv_store(%test,'x',$x);
40843003dfeSmillert    is($test{x},'foo','hv_store() stored');
40943003dfeSmillert    $test{x}='bar';
41043003dfeSmillert    is($x,'bar','hv_store() aliased');
41143003dfeSmillert    is($test{x},'bar','hv_store() aliased and stored');
41243003dfeSmillert}
41343003dfeSmillert
41443003dfeSmillert{
41543003dfeSmillert    my %hash=map { $_ => 1 } qw( a b c d e f);
41643003dfeSmillert    delete $hash{c};
41743003dfeSmillert    lock_keys(%hash);
41843003dfeSmillert    ok(Internals::SvREADONLY(%hash),'lock_keys DDS/t 1');
41943003dfeSmillert    delete @hash{qw(b e)};
42043003dfeSmillert    my @hidden=sort(hidden_keys(%hash));
42143003dfeSmillert    my @legal=sort(legal_keys(%hash));
42243003dfeSmillert    my @keys=sort(keys(%hash));
42343003dfeSmillert    #warn "@legal\n@keys\n";
42443003dfeSmillert    is("@hidden","b e",'lock_keys @hidden DDS/t');
42543003dfeSmillert    is("@legal","a b d e f",'lock_keys @legal DDS/t');
42643003dfeSmillert    is("@keys","a d f",'lock_keys @keys DDS/t');
42743003dfeSmillert}
42843003dfeSmillert{
42943003dfeSmillert    my %hash=(0..9);
43043003dfeSmillert    lock_keys(%hash);
43143003dfeSmillert    ok(Internals::SvREADONLY(%hash),'lock_keys DDS/t 2');
43243003dfeSmillert    Hash::Util::unlock_keys(%hash);
43343003dfeSmillert    ok(!Internals::SvREADONLY(%hash),'unlock_keys DDS/t 2');
43443003dfeSmillert}
43543003dfeSmillert{
43643003dfeSmillert    my %hash=(0..9);
43743003dfeSmillert    lock_keys(%hash,keys(%hash),'a'..'f');
43843003dfeSmillert    ok(Internals::SvREADONLY(%hash),'lock_keys args DDS/t');
439b8851fccSafresh1    my @hidden=sort numbers_first hidden_keys(%hash);
440b8851fccSafresh1    my @legal=sort numbers_first legal_keys(%hash);
441b8851fccSafresh1    my @keys=sort numbers_first keys(%hash);
44243003dfeSmillert    is("@hidden","a b c d e f",'lock_keys() @hidden DDS/t 3');
44343003dfeSmillert    is("@legal","0 2 4 6 8 a b c d e f",'lock_keys() @legal DDS/t 3');
44443003dfeSmillert    is("@keys","0 2 4 6 8",'lock_keys() @keys');
44543003dfeSmillert}
44643003dfeSmillert{
44743003dfeSmillert    my %hash=map { $_ => 1 } qw( a b c d e f);
44843003dfeSmillert    delete $hash{c};
44943003dfeSmillert    lock_ref_keys(\%hash);
45043003dfeSmillert    ok(Internals::SvREADONLY(%hash),'lock_ref_keys DDS/t');
45143003dfeSmillert    delete @hash{qw(b e)};
45243003dfeSmillert    my @hidden=sort(hidden_keys(%hash));
45343003dfeSmillert    my @legal=sort(legal_keys(%hash));
45443003dfeSmillert    my @keys=sort(keys(%hash));
45543003dfeSmillert    #warn "@legal\n@keys\n";
45643003dfeSmillert    is("@hidden","b e",'lock_ref_keys @hidden DDS/t 1');
45743003dfeSmillert    is("@legal","a b d e f",'lock_ref_keys @legal DDS/t 1');
45843003dfeSmillert    is("@keys","a d f",'lock_ref_keys @keys DDS/t 1');
45943003dfeSmillert}
46043003dfeSmillert{
46143003dfeSmillert    my %hash=(0..9);
46243003dfeSmillert    lock_ref_keys(\%hash,keys %hash,'a'..'f');
46343003dfeSmillert    ok(Internals::SvREADONLY(%hash),'lock_ref_keys args DDS/t');
464b8851fccSafresh1    my @hidden=sort numbers_first hidden_keys(%hash);
465b8851fccSafresh1    my @legal=sort numbers_first legal_keys(%hash);
466b8851fccSafresh1    my @keys=sort numbers_first keys(%hash);
46743003dfeSmillert    is("@hidden","a b c d e f",'lock_ref_keys() @hidden DDS/t 2');
46843003dfeSmillert    is("@legal","0 2 4 6 8 a b c d e f",'lock_ref_keys() @legal DDS/t 2');
46943003dfeSmillert    is("@keys","0 2 4 6 8",'lock_ref_keys() @keys DDS/t 2');
47043003dfeSmillert}
47143003dfeSmillert{
47243003dfeSmillert    my %hash=(0..9);
47343003dfeSmillert    lock_ref_keys_plus(\%hash,'a'..'f');
47443003dfeSmillert    ok(Internals::SvREADONLY(%hash),'lock_ref_keys_plus args DDS/t');
475b8851fccSafresh1    my @hidden=sort numbers_first hidden_keys(%hash);
476b8851fccSafresh1    my @legal=sort numbers_first legal_keys(%hash);
477b8851fccSafresh1    my @keys=sort numbers_first keys(%hash);
47843003dfeSmillert    is("@hidden","a b c d e f",'lock_ref_keys_plus() @hidden DDS/t');
47943003dfeSmillert    is("@legal","0 2 4 6 8 a b c d e f",'lock_ref_keys_plus() @legal DDS/t');
48043003dfeSmillert    is("@keys","0 2 4 6 8",'lock_ref_keys_plus() @keys DDS/t');
48143003dfeSmillert}
48243003dfeSmillert{
48391f110e0Safresh1    my %hash=(0..9, 'a' => 'alpha');
48491f110e0Safresh1    lock_ref_keys_plus(\%hash,'a'..'f');
48591f110e0Safresh1    ok(Internals::SvREADONLY(%hash),'lock_ref_keys_plus args overlap');
486b8851fccSafresh1    my @hidden=sort numbers_first hidden_keys(%hash);
487b8851fccSafresh1    my @legal=sort numbers_first legal_keys(%hash);
488b8851fccSafresh1    my @keys=sort numbers_first keys(%hash);
48991f110e0Safresh1    is("@hidden","b c d e f",'lock_ref_keys_plus() @hidden overlap');
49091f110e0Safresh1    is("@legal","0 2 4 6 8 a b c d e f",'lock_ref_keys_plus() @legal overlap');
49191f110e0Safresh1    is("@keys","0 2 4 6 8 a",'lock_ref_keys_plus() @keys overlap');
49291f110e0Safresh1}
49391f110e0Safresh1{
49443003dfeSmillert    my %hash=(0..9);
49543003dfeSmillert    lock_keys_plus(%hash,'a'..'f');
49643003dfeSmillert    ok(Internals::SvREADONLY(%hash),'lock_keys_plus args DDS/t');
497b8851fccSafresh1    my @hidden=sort numbers_first hidden_keys(%hash);
498b8851fccSafresh1    my @legal=sort numbers_first legal_keys(%hash);
499b8851fccSafresh1    my @keys=sort numbers_first keys(%hash);
50043003dfeSmillert    is("@hidden","a b c d e f",'lock_keys_plus() @hidden DDS/t 3');
50143003dfeSmillert    is("@legal","0 2 4 6 8 a b c d e f",'lock_keys_plus() @legal DDS/t 3');
50243003dfeSmillert    is("@keys","0 2 4 6 8",'lock_keys_plus() @keys DDS/t 3');
50343003dfeSmillert}
50491f110e0Safresh1{
50591f110e0Safresh1    my %hash=(0..9, 'a' => 'alpha');
50691f110e0Safresh1    lock_keys_plus(%hash,'a'..'f');
50791f110e0Safresh1    ok(Internals::SvREADONLY(%hash),'lock_keys_plus args overlap non-ref');
508b8851fccSafresh1    my @hidden=sort numbers_first hidden_keys(%hash);
509b8851fccSafresh1    my @legal=sort numbers_first legal_keys(%hash);
510b8851fccSafresh1    my @keys=sort numbers_first keys(%hash);
51191f110e0Safresh1    is("@hidden","b c d e f",'lock_keys_plus() @hidden overlap non-ref');
51291f110e0Safresh1    is("@legal","0 2 4 6 8 a b c d e f",'lock_keys_plus() @legal overlap non-ref');
51391f110e0Safresh1    is("@keys","0 2 4 6 8 a",'lock_keys_plus() @keys overlap non-ref');
51491f110e0Safresh1}
51543003dfeSmillert
51643003dfeSmillert{
51743003dfeSmillert    my %hash = ('a'..'f');
51843003dfeSmillert    my @keys = ();
51943003dfeSmillert    my @ph = ();
52043003dfeSmillert    my @lock = ('a', 'c', 'e', 'g');
52143003dfeSmillert    lock_keys(%hash, @lock);
52243003dfeSmillert    my $ref = all_keys(%hash, @keys, @ph);
52343003dfeSmillert    my @crrack = sort(@keys);
52443003dfeSmillert    my @ooooff = qw(a c e);
52543003dfeSmillert    my @bam = qw(g);
52643003dfeSmillert
52743003dfeSmillert    ok(ref $ref eq ref \%hash && $ref == \%hash,
52843003dfeSmillert            "all_keys() - \$ref is a reference to \%hash");
52943003dfeSmillert    is_deeply(\@crrack, \@ooooff, "Keys are what they should be");
53043003dfeSmillert    is_deeply(\@ph, \@bam, "Placeholders in place");
53143003dfeSmillert}
53243003dfeSmillert
53391f110e0Safresh1{
534b8851fccSafresh1    # lock_hash_recurse / unlock_hash_recurse
53591f110e0Safresh1    my %hash = (
53691f110e0Safresh1        a   => 'alpha',
53791f110e0Safresh1        b   => [ qw( beta gamma delta ) ],
53891f110e0Safresh1        c   => [ 'epsilon', { zeta => 'eta' }, ],
53991f110e0Safresh1        d   => { theta => 'iota' },
54091f110e0Safresh1    );
54191f110e0Safresh1    lock_hash_recurse(%hash);
54291f110e0Safresh1    ok( hash_locked(%hash),
54391f110e0Safresh1        "lock_hash_recurse(): top-level hash locked" );
54491f110e0Safresh1    ok( hash_locked(%{$hash{d}}),
54591f110e0Safresh1        "lock_hash_recurse(): element which is hashref locked" );
54691f110e0Safresh1    ok( ! hash_locked(%{$hash{c}[1]}),
54791f110e0Safresh1        "lock_hash_recurse(): element which is hashref in array ref not locked" );
54891f110e0Safresh1
54991f110e0Safresh1    unlock_hash_recurse(%hash);
55091f110e0Safresh1    ok( hash_unlocked(%hash),
55191f110e0Safresh1        "unlock_hash_recurse(): top-level hash unlocked" );
55291f110e0Safresh1    ok( hash_unlocked(%{$hash{d}}),
55391f110e0Safresh1        "unlock_hash_recurse(): element which is hashref unlocked" );
554b8851fccSafresh1    {
555b8851fccSafresh1        local $@;
556b8851fccSafresh1        eval { $hash{d} = { theta => 'kappa' }; };
557b8851fccSafresh1        ok(! $@, "No error; can assign to unlocked hash")
558b8851fccSafresh1            or diag($@);
559b8851fccSafresh1    }
560b8851fccSafresh1    ok( hash_unlocked(%{$hash{c}[1]}),
561b8851fccSafresh1        "unlock_hash_recurse(): element which is hashref in array ref not locked" );
562b8851fccSafresh1}
563b8851fccSafresh1
564b8851fccSafresh1{
565b8851fccSafresh1    # lock_hashref_recurse / unlock_hashref_recurse
566b8851fccSafresh1    my %hash = (
567b8851fccSafresh1        a   => 'alpha',
568b8851fccSafresh1        b   => [ qw( beta gamma delta ) ],
569b8851fccSafresh1        c   => [ 'epsilon', { zeta => 'eta' }, ],
570b8851fccSafresh1        d   => { theta => 'iota' },
571b8851fccSafresh1    );
572b8851fccSafresh1    Hash::Util::lock_hashref_recurse(\%hash);
573b8851fccSafresh1    ok( hash_locked(%hash),
574b8851fccSafresh1        "lock_hash_recurse(): top-level hash locked" );
575b8851fccSafresh1    ok( hash_locked(%{$hash{d}}),
576b8851fccSafresh1        "lock_hash_recurse(): element which is hashref locked" );
577b8851fccSafresh1    ok( ! hash_locked(%{$hash{c}[1]}),
578b8851fccSafresh1        "lock_hash_recurse(): element which is hashref in array ref not locked" );
579b8851fccSafresh1
580b8851fccSafresh1    Hash::Util::unlock_hashref_recurse(\%hash);
581b8851fccSafresh1    ok( hash_unlocked(%hash),
582b8851fccSafresh1        "unlock_hash_recurse(): top-level hash unlocked" );
583b8851fccSafresh1    ok( hash_unlocked(%{$hash{d}}),
584b8851fccSafresh1        "unlock_hash_recurse(): element which is hashref unlocked" );
585b8851fccSafresh1    {
586b8851fccSafresh1        local $@;
587b8851fccSafresh1        eval { $hash{d} = { theta => 'kappa' }; };
588b8851fccSafresh1        ok(! $@, "No error; can assign to unlocked hash")
589b8851fccSafresh1            or diag($@);
590b8851fccSafresh1    }
59191f110e0Safresh1    ok( hash_unlocked(%{$hash{c}[1]}),
59291f110e0Safresh1        "unlock_hash_recurse(): element which is hashref in array ref not locked" );
59391f110e0Safresh1}
59491f110e0Safresh1
59591f110e0Safresh1{
59691f110e0Safresh1    my $h1= hash_value("foo");
59791f110e0Safresh1    my $h2= hash_value("bar");
59891f110e0Safresh1    is( $h1, hash_value("foo") );
59991f110e0Safresh1    is( $h2, hash_value("bar") );
600*eac174f2Safresh1
601*eac174f2Safresh1    my $seed= hash_seed();
602*eac174f2Safresh1    my $h1s= hash_value("foo",$seed);
603*eac174f2Safresh1    my $h2s= hash_value("bar",$seed);
604*eac174f2Safresh1
605*eac174f2Safresh1    is( $h1s, hash_value("foo",$seed) );
606*eac174f2Safresh1    is( $h2s, hash_value("bar",$seed) );
607*eac174f2Safresh1
608*eac174f2Safresh1    $seed= join "", map { chr $_ } 1..length($seed);
609*eac174f2Safresh1
610*eac174f2Safresh1    my $h1s2= hash_value("foo",$seed);
611*eac174f2Safresh1    my $h2s2= hash_value("bar",$seed);
612*eac174f2Safresh1
613*eac174f2Safresh1    is( $h1s2, hash_value("foo",$seed) );
614*eac174f2Safresh1    is( $h2s2, hash_value("bar",$seed) );
615*eac174f2Safresh1
616*eac174f2Safresh1    isnt($h1s,$h1s2);
617*eac174f2Safresh1    isnt($h1s,$h1s2);
618*eac174f2Safresh1
61991f110e0Safresh1}
620*eac174f2Safresh1
62191f110e0Safresh1{
62291f110e0Safresh1    my @info1= bucket_info({});
62391f110e0Safresh1    my @info2= bucket_info({1..10});
62491f110e0Safresh1    my @stats1= bucket_stats({});
62591f110e0Safresh1    my @stats2= bucket_stats({1..10});
62691f110e0Safresh1    my $array1= bucket_array({});
62791f110e0Safresh1    my $array2= bucket_array({1..10});
62891f110e0Safresh1    is("@info1","0 8 0");
6299f11ffb7Safresh1    like("@info2[0,1]",qr/5 (?:8|16)/);
63091f110e0Safresh1    is("@stats1","0 8 0");
6319f11ffb7Safresh1    like("@stats2[0,1]",qr/5 (?:8|16)/);
63291f110e0Safresh1    my @keys1= sort map { ref $_ ? @$_ : () } @$array1;
63391f110e0Safresh1    my @keys2= sort map { ref $_ ? @$_ : () } @$array2;
63491f110e0Safresh1    is("@keys1","");
63591f110e0Safresh1    is("@keys2","1 3 5 7 9");
63691f110e0Safresh1}
637