The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use Test::More;
use Test::Exception;
use Catalyst ();
use FindBin;
use Path::Class::File;
use Imager;
use Image::Info qw(image_info image_type dim);

# setup our Catalyst :-)
my $c = Catalyst->new();
$c->setup_log();
$c->setup_home("$FindBin::Bin");

{
    no warnings;
    
    # patch our Catalyst environment for testing
    package Catalyst;
    my %stash;
    use Moose;
    no warnings;
    __PACKAGE__->meta->make_mutable;
    sub forward {
        my ($c, $action, @args) = @_;
        $action->{code}->($action->{self}, $c, @args);
    }
    sub stash {
        my $self = shift;
        while (@_) {
            my $key = shift;
            my $value = shift;
            $stash{$key} = $value;
        }
        
        return \%stash;
    }
    around dispatch => sub {
        my $orig = shift;
        %stash = ();
        $orig->dispatch(@_);
    };
    __PACKAGE__->meta->make_immutable;
    
    package Catalyst::Controller::Imager;
    sub action_for {
        my ($self, $name) = @_;
        if ($self->can($name)) {
            return {
                code => $self->can($name),
                self => $self,
            };
        }
        die "cannot find $name as sub...";
    };
}

#
# check available imager formats
#
ok(scalar(keys(%Imager::formats)) > 0, 'Imager.pm tells some formats');
ok(exists($Imager::formats{png}),  'png format is possible');
ok(exists($Imager::formats{gif}),  'gif format is possible');
ok(exists($Imager::formats{jpeg}), 'jpeg format is possible');

BAIL_OUT('Imager.pm not configured as expected - please reinstall with gif, jpeg and png support!')
    if (!exists($Imager::formats{png}) || 
        !exists($Imager::formats{gif}) ||
        !exists($Imager::formats{jpeg}) );

#
# test basic things
#
# can we use it?
use_ok('Catalyst::Controller::Imager');
can_ok('Catalyst::Controller::Imager' => qw(base scale image convert_image 
                                            end 
                                            want_w want_h want_thumbnail));

# instantiate
my $controller;
lives_ok { $controller = Catalyst->setup_component('Catalyst::Controller::Imager') }
         'setup component worked';
is(ref($controller), 'Catalyst::Controller::Imager', 'controller class looks good');

# check default attributes
is($controller->root_dir,       'static/images', 'default root directory looks good');
is($controller->cache_dir,      undef,           'default cache directory looks good');
is($controller->default_format, 'jpg',           'default format sub looks good');
is($controller->max_size,       1000,            'default max size looks good');
is($controller->thumbnail_size, 80,              'default thumbnail size looks good');

#
# base() checking
#
%{ $c->stash } = ();
is_deeply($c->stash, {}, 'stash is empty');
lives_ok { $controller->base($c) } 'base() can be called';
is_deeply($c->stash, 
          { 
              image_path   => [],
              image        => undef,
              image_data   => undef,
              cache_path   => [],
              scale        => {w => undef, h => undef, mode => 'min'},
              format       => undef,
              before_scale => [],
              after_scale  => [],
          },
          'stash is initially set');

####
# scale() and other methods can not non-trivially checked in a simple manner...
####

#
# get a not existing file
#
$c->stash(
    image_path => ['rails_logo.png'],
    format     => 'png',
    image_data => undef,
);
dies_ok { $controller->convert_image($c) }
         'unknown file retrieval dies';

#
# test image conversion calling it directly
#
# try to load catalyst logo
# original size = 171 x 244 pix
# original format = png
my @test_cases = (
    # original size, different formats
    { format => 'png',  scale => {w => undef, h => undef, mode => 'min'},
      type   => 'PNG',  dim => [171,244], size => 10000 },
    { format => 'jpeg', scale => {w => undef, h => undef, mode => 'min'},
      type   => 'JPEG', dim => [171,244], size => 1000 },
    { format => 'gif',  scale => {w => undef, h => undef, mode => 'min'},
      type   => 'GIF',  dim => [171,244], size => 8000 },

    # width set, different formats
    { format => 'png',  scale => {w => 200, h => undef, mode => 'min'},
      type   => 'PNG',  dim => [200,285], size => 10000 },
    { format => 'jpeg', scale => {w => 200, h => undef, mode => 'min'},
      type   => 'JPEG', dim => [200,285], size => 1000 },
    { format => 'gif',  scale => {w => 200, h => undef, mode => 'min'},
      type   => 'GIF',  dim => [200,285], size => 10000 },

    # height set, different formats
    { format => 'png',  scale => {w => undef, h => 200, mode => 'min'},
      type   => 'PNG',  dim => [140,200], size => 10000 },
    { format => 'jpeg', scale => {w => undef, h => 200, mode => 'min'},
      type   => 'JPEG', dim => [140,200], size => 1000 },
    { format => 'gif',  scale => {w => undef, h => 200, mode => 'min'},
      type   => 'GIF',  dim => [140,200], size => 5000 },

    # width+height set, oversized width
    { format => 'png',  scale => {w => 400, h => 150, mode => 'min'},
      type   => 'PNG',  dim => [105,150], size => 10000 },
    { format => 'jpeg', scale => {w => 400, h => 150, mode => 'min'},
      type   => 'JPEG', dim => [105,150], size => 1000 },
    { format => 'gif',  scale => {w => 400, h => 150, mode => 'min'},
      type   => 'GIF',  dim => [105,150], size => 6000 },
);

foreach my $test_case (@test_cases) {
    $c->stash(
        image_path => ['catalyst_logo.png'],
        format     => $test_case->{format},
        image_data => undef,
        scale      => { %{ $test_case->{scale} } },
    );
    my $name = "$test_case->{format} $test_case->{dim}->[0]x$test_case->{dim}->[1]";

    lives_ok { $controller->convert_image($c) }
             "$name lives";
    ok(length($c->stash->{image_data}) > $test_case->{size}, "$name size is reasonable (${\length($c->stash->{image_data})} > $test_case->{size})");
    file_type_is($name, $test_case->{type});
    file_dimension_is($name, @{$test_case->{dim}});
}

done_testing;

#################################################
#
# helper subs
#
sub file_type_is {
    my $name = shift;
    my $format = shift;
    
    my $image_type = image_type(\do{ $c->stash->{image_data} });
    ok(ref($image_type) eq 'HASH' &&
       exists($image_type->{file_type}) &&
       $image_type->{file_type} eq $format, "$name is '$format'");
}

sub file_dimension_is {
    my $name = shift;
    my $w = shift;
    my $h = shift;

    is_deeply([dim(image_info(\do { $c->stash->{image_data} }))], [$w, $h], "$name is $w x $h");
}