#  Tests for self referential tree save and reload where most refs to the root are weakened.
use strict;
use warnings;
use File::Spec;
use Scalar::Util qw /weaken/;

#use Sereal ();
use Sereal::Encoder;

local $| = 1;

use lib File::Spec->catdir(qw(t lib));
BEGIN {
    lib->import('lib')
        if !-d 't';
}

use Sereal::TestSet qw(:all);
use Sereal::BulkTest qw(:all);
use Test::More;


my $ok = have_encoder_and_decoder();
if (not $ok) {
    plan skip_all => 'Did not find right version of decoder';
}
else {
    run_weakref_tests();
}


sub run_weakref_tests {
    #  Child to parent refs are weak, root node is stored once in the hash
    #  Was failing on x64 Strawberry perls 5.16.3, 5.18.4, 5.20.1
    test_save_and_reload ();
    
    #  Child to parent refs are weak, but we store the root node twice in the hash
    #  (second time is in the "TREE_BY_NAME" subhash)
    #  Was failing on x64 Strawberry perls 5.16.3, passing on 5.18.4, 5.20.1
    test_save_and_reload (store_root_by_name => 1);
    
    #  child to parent refs are strong
    #  Should pass
    test_save_and_reload (no_weaken_refs => 1);
}

pass();
done_testing();

exit;


sub get_data {
    my %args = @_;

    my @children;

    my $root = {
        name     => 'root',
        children => \@children,
    };

    my %hash = (
        TREE => $root,
        TREE_BY_NAME => {},
    );

    if ($args{store_root_by_name}) {
        $hash{TREE_BY_NAME}{root} = $root;
    }

    foreach my $i (0 .. 1) {
        my $child = {
            PARENT => $root,
            NAME => $i,
        };

        if (!$args{no_weaken_refs}) {
            weaken $child->{PARENT};
        }

        push @children, $child;
        #  store it in the by-name cache
        $hash{TREE_BY_NAME}{$i} = $child;
    }

    return \%hash;
}


sub test_save_and_reload {
    my %args = @_;
    my $data = get_data (%args);

    #diag '=== ARGS ARE:  ' . join ' ', %args;

    my $context_text;
    $context_text .= $args{no_weaken} ? 'not weakened' : 'weakened';
    $context_text .= $args{store_root_by_name}
        ? ', extra root ref stored'
        : ', extra root ref not stored';

    my $encoder = Sereal::Encoder->new;
    my $decoder = Sereal::Decoder->new;
    my ($encoded_data, $decoded_data);

    $encoded_data = eval {$encoder->encode($data)};
    my $e = $@;
    ok (!$e, "Encoded without exception, $context_text");

    #  no point testing if serialisation failed
    if ($encoded_data) {
        eval {$decoder->decode ($encoded_data, $decoded_data)};
        my $e = $@;
        ok (!$e, "Decoded using Sereal, $context_text");

        is_deeply (
            $decoded_data,
            $data,
            "Data structures match, $context_text",
        );
    }

}


1;
