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

use strict;
use warnings;

use Test::More tests => 3 + 7 * 4;

use lib 't/lib';

use LaTeX::TikZ::Meta::TypeConstraint::Autocoerce;

{
 my $tc = eval {
  LaTeX::TikZ::Meta::TypeConstraint::Autocoerce->new(
   target => undef,
  );
 };
 like $@, qr/^No meta object for undefined target/, 'Undef target';
}

{
 my $target = 'LaTeX::TikZ::A::Class::Likely::Not::To::Exist';
 my $tc = eval {
  LaTeX::TikZ::Meta::TypeConstraint::Autocoerce->new(
   target => $target,
  );
 };
 like $@, qr/^No meta object for target \Q$target\E/, 'Nonexistent target';
}

{
 my $tc = eval {
  LaTeX::TikZ::Meta::TypeConstraint::Autocoerce->new(
   target => 'LaTeX::TikZ::Meta::TypeConstraint::Autocoerce',
  );
 };
 is $@, '', 'Valid target';
}

{
 package LaTeX::TikZ::TestX;

 use Mouse;
 use Mouse::Util::TypeConstraints qw<
  coerce from via
  find_type_constraint
  register_type_constraint
 >;

 has 'id' => (
  is       => 'ro',
  isa      => 'Int',
  required => 1,
 );

 register_type_constraint(
  LaTeX::TikZ::Meta::TypeConstraint::Autocoerce->new(
   name   => 'LaTeX::TikZ::TestX::Autocoerce',
   target => find_type_constraint(__PACKAGE__),
   mapper => sub {
    shift;
    my ($last) = $_[0] =~ /::([^:]+)$/;
    join '::', __PACKAGE__, "From$last";
   },
  ),
 );

 coerce 'LaTeX::TikZ::TestX::Autocoerce'
     => from 'LaTeX::TikZ::TestX'
     => via { $_ };

 coerce 'LaTeX::TikZ::TestX::Autocoerce'
     => from 'Int'
     => via { __PACKAGE__->new(id => $_) };

 __PACKAGE__->meta->make_immutable;

 sub main::X () { __PACKAGE__ }
}

{
 package LaTeX::TikZ::TestY;

 use Mouse;

 has 'num' => (
  is       => 'ro',
  isa      => 'Num',
  required => 1,
 );

 __PACKAGE__->meta->make_immutable;

 sub main::Y () { __PACKAGE__ }
}

{
 package LaTeX::TikZ::TestZ;

 use Mouse;

 has 'x' => (
  is       => 'ro',
  isa      => 'LaTeX::TikZ::TestX::Autocoerce',
  required => 1,
  coerce   => 1,
 );

 __PACKAGE__->meta->make_immutable;

 sub main::Z () { __PACKAGE__ }
}

{
 package LaTeX::TikZ::TestW;

 use Mouse;
 use Mouse::Util::TypeConstraints;

 has 'x' => (
  is       => 'ro',
  isa      => 'LaTeX::TikZ::TestX',
  required => 1,
 );

 coerce 'LaTeX::TikZ::TestX::Autocoerce'
     => from +(__PACKAGE__)
     => via { $_->x };

 __PACKAGE__->meta->make_immutable;

 sub main::W () { __PACKAGE__ }
}

my $y = Y->new(
 num => '3.14159',
);

my $y2 = Y->new(
 num => exp(1),
);

my $time = time;
my $x0 = X->new(
 id => $time,
);

my $w = W->new(
 x => $x0,
);

my @tests = (
 [ 123, 123,   'autocoerce X from int'       ],
 [ $x0, $time, 'autocoerce X from X'         ],
 [ $x0, $time, 'autocoerce X from X twice'   ],
 [ $y,  3,     'autocoerce X from Y'         ],
 [ $y2, 2,     'autocoerce X from another Y' ],
 [ $w,  $time, 'autocoerce X from W'         ],
 [ $w,  $time, 'autocoerce X from W twice'   ],
);

for my $test (@tests) {
 my ($x, $exp, $desc) = @$test;
 my $z = eval {
  Z->new(x => $x);
 };
 my $err = $@;
 if (ref $exp eq 'Regexp') {
  like $err, $exp, "could not $desc";
  fail "$desc placeholder $_" for 1 .. 3;
 } else {
  is     $err,   '',   "$desc doesn't croak";
  isa_ok $z,     Z(),  "$desc returns a Z object";
  $x = $z->x;
  isa_ok $x,     X(),  "$desc stores an X into the Z object";
  is     $x->id, $exp, "$desc correctly";
 }
}