The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use perl5i::latest;
use Test::More;

note "each with no signature"; {
    my %want = (foo => 23, bar => 42);

    my %have;
    %want->each( sub { $have{$_[0]} = $_[1] } );

    is_deeply \%have, \%want;
}


note "each with one arg"; {
    my %want = (foo => 23, bar => 42);

    my %have;
    %want->each( func($k) { $have{$k} = 1 } );

    is_deeply \%have, { foo => 1, bar => 1 };
}


note "each with two args"; {
    my %want = (foo => 23, bar => 42);

    my %have;
    %want->each( func($k,$v) { $have{$k} = $v } );

    is_deeply \%have, \%want;
}


note "each call is safe"; {
    my %want = (foo => 23, bar => 42, baz => 99, biff => 66);

    # Call each once on %want to start the iterator attached to %want
    my($k,$v) = each %want;

    my %have;
    %want->each( func($k,$v) { $have{$k} = $v } );

    is_deeply \%have, \%want;
}

note "Tests adapted from Hash::StoredIterator";

my @want_outer = (
    [a => 1],
    [b => 2],
    [c => 3],
);

my @want_inner = (
    [a => 1],
    [a => 1],
    [a => 1],
    [b => 2],
    [b => 2],
    [b => 2],
    [c => 3],
    [c => 3],
    [c => 3],
);

my %hash = ( a => 1, b => 2, c => 3 );

sub interference {
    my @garbage = keys(%hash), values(%hash);
    while ( my ( $k, $v ) = each(%hash) ) {
        # Effectively do nothing
        my $foo = $k . $v;
    }
};

{
    my @inner;
    my @outer;

    %hash->each( func( $k, $v ) {
        ok( $k, "Got key" );
        ok( $v, "Got val" );
        is( $k, $_, '$_ is set to key' );
        is( $k, $a, '$a is set to key' );
        is( $v, $b, '$b is set to val' );

        push @outer => [$k, $v];
        interference();

        %hash->each( func( $k2, $v2 ) {
            is( $k2, $_, '$_ is set to key' );
            is( $k2, $a, '$a is set to key' );
            is( $v2, $b, '$b is set to val' );

            push @inner => [$k, $v];

            interference();
        });

        is( $k, $_, '$_ is not squashed by inner loop' );
        is( $k, $a, '$a is not squashed by inner loop' );
        is( $v, $b, '$a is not squashed by inner loop' );
    });

    is_deeply(
        [sort { $a->[0] cmp $b->[0] } @outer],
        \@want_outer,
        "Outer loop got all keys"
    );

    is_deeply(
        [sort { $a->[0] cmp $b->[0] } @inner],
        \@want_inner,
        "Inner loop got all keys multiple times"
    );
}

done_testing;