The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#! /usr/bin/perl
#---------------------------------------------------------------------
# Copyright 2010 Christopher J. Madsen
#
# Author: Christopher J. Madsen <perl@cjmweb.net>
# Created: 02 Jun 2010
#
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either the
# GNU General Public License or the Artistic License for more details.
#
# Test MooseX::AttributeTree on container/leaf classes
#---------------------------------------------------------------------

use Test::More 0.88 tests => 140; # done_testing

use MooseX::AttributeTree;

#=====================================================================
# Classes for testing:

{
  package My_Container;

  use Moose;
  use MooseX::AttributeTree ();

  has parent => (
    is       => 'rw',
    isa      => 'Maybe[Object]',
    weak_ref => 1,
  );

  has values => (
    is     => 'ro',
    isa    => 'HashRef',
  );

  sub get_value
  {
    my ($self, $attribute) = @_;

    # See if we have the attribute:
    my $values = $self->values;

    return $values->{$attribute} if $values and exists $values->{$attribute};

    # We don't have it, ask our parent:
    my $parent = $self->parent;
    return ($parent ? $parent->get_value($attribute) : undef);
  } # end get_value
} # end My_Container

#---------------------------------------------------------------------
{
  package My_Leaf;

  use Moose;
  use MooseX::AttributeTree ();

  has parent => (
    is       => 'rw',
    isa      => 'Maybe[Object]',
    weak_ref => 1,
  );

  has value => (
    is     => 'rw',
    predicate => 'has_value',
    clearer   => 'clear_value',
    traits   => [ TreeInherit => {
      fetch_method => 'get_value',
    } ],
  );

  has ro_value => (
    is     => 'ro',
    predicate => 'has_ro_value',
    clearer   => 'clear_ro_value',
    traits   => [ TreeInherit => {
      fetch_method => 'get_value',
      default      => 'default ro_value',
    } ],
  );
} # end My_Leaf

#=====================================================================
# Create the node hierarchy:

my $hierarchy = [
  root => { value => 'root value', ro_value => 'root ro_value' },
  [ a => {},
    [ aa => { value => 'aa value' },
      [ aaa => { ro_value => 'aaa ro_value' } ],
      [ aab => { value => 'aab value' } ] ],
    [ ab => {} ] ],
  [ b => { value => 'b value', ro_value => 'b ro_value' } ],
];

my @values = qw(
  root root root
  a    root root
  aa   aa   root
  aaa  aa   aaa
  aab  aab  root
  ab   root root
  b    b    b
);

my %node;

sub build_hierarchy
{
  my ($data, $parent) = @_;

  my $name = shift @$data;
  my $init = shift @$data;

  if (@$data) {
    # Create a container node:
    my $node = $node{$name} = My_Container->new(parent => $parent,
                                                values => $init);

    foreach my $child (@$data) {
      build_hierarchy($child, $node);
    } # end foreach $child
  } else {
    # Create a leaf node:
    $init->{parent} = $parent;
    $node{$name} = My_Leaf->new($init);
  } # end else leaf node
} # end build_hierarchy

build_hierarchy($hierarchy);

#=====================================================================
# Check values:

sub check_values
{
  my $testName = shift;

  while (@_) {
    my $name     = shift;
    my $value    = shift;
    my $ro_value = shift;

    my $node = $node{$name};

    if ($node->isa('My_Container')) {
      is($node->get_value('value'), "$value value", "$testName $name value");
      is(defined($node->values->{value}), $name eq lc $value,
         "$testName $name has_value");
      is($node->get_value('ro_value'),
         ($ro_value eq 'default' ? undef : "$ro_value ro_value"),
         "$testName $name ro_value");
      is(defined($node->values->{ro_value}), $name eq lc $ro_value,
         "$testName $name has_ro_value");
    } else {
      is($node->value,    "$value value",       "$testName $name value");
      is($node->has_value, $name eq lc $value,  "$testName $name has_value");
      is($node->ro_value, "$ro_value ro_value", "$testName $name ro_value");
      is($node->has_ro_value, $name eq lc $ro_value,
         "$testName $name has_ro_value");
    } # end else leaf node
  } # end while @_
} # end check_values

check_values('initial', @values);

#---------------------------------------------------------------------

$node{root}->values->{value} = 'ROOT value';

for my $i (0 .. $#values) {
  $values[$i] =~ s/root/ROOT/ if $i % 3 == 1;
}

check_values('root change', @values);

#---------------------------------------------------------------------
$node{a}->values->{value} = 'A value';

@values = qw(
  root ROOT root
  a    A    root
  aa   aa   root
  aaa  aa   aaa
  aab  aab  root
  ab   A    root
  b    b    b
);

check_values('a set', @values);

#---------------------------------------------------------------------
delete $node{aa}->values->{value};

for my $i (0 .. $#values) {
  $values[$i] =~ s/^aa$/A/ if $i % 3 == 1;
}

check_values('aa cleared', @values);

#---------------------------------------------------------------------
delete $node{root}->values->{ro_value};
$node{b}->clear_ro_value;

for my $i (0 .. $#values) {
  $values[$i] =~ s/^(?:root|b)$/default/ if $i % 3 == 2;
}

check_values('b cleared', @values);

done_testing;