package tests::TestMockTest;

use strict;

use base qw/Lire::Test::TestCase/;

use Lire::Test::Mock;

sub new {
    return shift->SUPER::new( @_ );
}

sub set_up {
    my $self = $_[0];
    $self->SUPER::set_up();

    $self->{'mock'} = new Lire::Test::Mock( 'FakePackage' );
    return;
}

sub tear_down {
    my $self = $_[0];
    $self->SUPER::tear_down();

    return;
}

sub test_new {
    my $self = $_[0];

    my $mock = new Lire::Test::Mock( 'FakePackage', 'method_1' => 1 );
    $self->assert_isa( 'FakePackage', $mock );
    $self->assert_deep_equals( bless( {
                                       '_base' => 'FakePackage',
                                       '_calls' => [],
                                       '_proxy' => 0,
                                       '_invocations' =>
                                       { 'method_1' => [],
                                         'method_2'  => [],
                                       },
                                       '_results' => { 'method_1' => 1 },
                                      }, ref $self->{'mock'} ),
                               $mock );
}

sub test_new_proxy {
    my $self = $_[0];

    my $mock = new_proxy Lire::Test::Mock( 'FakePackage', 1, 2, 3 );
    $self->assert_isa( 'FakePackage', $mock );
    $self->assert_deep_equals( bless( {
                                       'args' => [ 1, 2, 3 ],
                                       '_base' => 'FakePackage',
                                       '_calls' => [],
                                       '_proxy' => 1,
                                       '_invocations' =>
                                       { 'method_1' => [],
                                         'method_2'  => [],
                                       },
                                       '_results' => {},
                                      }, ref $self->{'mock'} ),
                               $mock );

    $self->assert_str_equals( 'method_1', $mock->method_1() );
    $self->assert_str_equals( 'method_2', $mock->method_2() );
    $mock->set_result( 'method_1', 'shadow' );
    $self->assert_str_equals( 'shadow', $mock->method_1() );
    $mock->set_result( 'method_1', undef );
    $self->assert_str_equals( 'method_1', $mock->method_1() );
    $self->assert_deep_equals( [ 'method_1', 'method_2',
                                 'method_1', 'method_1' ],
                               $mock->get_calls() );

}

sub test_new_proxy_instance {
    my $self = $_[0];

    my $fake = FakePackage->new( 1, 2, 3 );
    my $mock = new_proxy Lire::Test::Mock( $fake );
    $self->assert_isa( 'FakePackage', $mock );
    $self->assert_deep_equals( bless( {
                                       'args' => [ 1, 2, 3 ],
                                       '_base' => 'FakePackage',
                                       '_calls' => [],
                                       '_proxy' => 1,
                                       '_invocations' =>
                                       { 'method_1' => [],
                                         'method_2'  => [],
                                       },
                                       '_results' => {},
                                      }, ref $self->{'mock'} ),
                               $mock );
}

sub test_set_mock_factory {
    my $self = $_[0];

    $self->assert_dies( qr/no new\(\) method in class A_Package/,
                        sub { Lire::Test::Mock->set_mock_factory( 'A_Package' ) } );

    no strict 'refs';

    my $or_factory = *{"BasePackage::new"}{'CODE'};
    $self->assert_str_equals( $or_factory, FakePackage->can( 'new' ) );
    $self->assert_deep_equals( {}, \%Lire::Test::Mock::FACTORIES );

    Lire::Test::Mock->set_mock_factory( 'FakePackage',  'method_1' => 'test' );
    $self->assert_deep_equals( { 'FakePackage' => undef },
                               \%Lire::Test::Mock::FACTORIES );
    $self->assert_deep_equals( { 'FakePackage' => { 'method_1', 'test' } },
                               \%Lire::Test::Mock::FACTORY_RESULTS );
    $self->assert_str_not_equals( $or_factory, FakePackage->can( 'new' ) );

    Lire::Test::Mock->set_mock_factory( 'FakePackage' );
    $self->assert_deep_equals( { 'FakePackage' => undef },
                               \%Lire::Test::Mock::FACTORIES );

    my $mock = FakePackage->new();
    $self->assert_isa( 'FakePackage', $mock );
    $self->assert_num_equals( 1, $mock->{'_proxy'} );
    $self->assert_num_equals( 1, $mock->invocation_count( 'new' ) );
    $self->assert_deep_equals( [ 'FakePackage' ],
                              $mock->get_invocation( 'new' ) );
    $self->assert_str_equals( 'test', $mock->method_1() );
    $self->assert_deep_equals( { 'FakePackage' => [ $mock ] },
                               \%Lire::Test::Mock::MOCK_INSTANCES );
    $self->assert_deep_equals(  [ $mock ],
                                Lire::Test::Mock->mock_instances( 'FakePackage' ) );

    $self->assert_dies( qr/no mock factory installed for class 'BasePackage'/,
                        sub {  Lire::Test::Mock->mock_instances( 'BasePackage' ) } );

    Lire::Test::Mock->set_mock_factory( 'BasePackage' );
    $self->assert_str_not_equals( $or_factory, BasePackage->can( 'new' ) );
    $self->assert_deep_equals( {},
                               $Lire::Test::Mock::FACTORY_RESULTS{'BasePackage'} );
    $self->assert_deep_equals( { 'BasePackage' => $or_factory,
                                 'FakePackage' => undef,
                               },
                               \%Lire::Test::Mock::FACTORIES );
    $self->assert_str_equals( 'method_1', BasePackage->new()->method_1() );

    Lire::Test::Mock->reset_factories();
    $self->assert_deep_equals( {}, \%Lire::Test::Mock::FACTORIES );
    $self->assert_deep_equals( {}, \%Lire::Test::Mock::FACTORY_RESULTS );
    $self->assert_deep_equals( {}, \%Lire::Test::Mock::MOCK_INSTANCES );
    $self->assert_str_equals( $or_factory, BasePackage->can( 'new' ) );
    $self->assert_str_equals( $or_factory, FakePackage->can( 'new' ) );
}

sub test_list_methods {
    my $self = $_[0];

    my @methods = Lire::Test::Mock::list_methods( 'BasePackage' );
    $self->assert_deep_equals( [ sort ( 'method_1', 'new' ) ],
                               [ sort @methods  ] );

    @methods = Lire::Test::Mock::list_methods( 'FakePackage' );
    $self->assert_deep_equals( [ sort ( 'method_1', 'method_2', 'new' ) ],
                               [ sort @methods ] );
}

sub test_get_calls {
    my $self = $_[0];

    $self->{'mock'}->method_1();
    $self->assert_deep_equals( [ 'method_1'  ],
                               $self->{'mock'}{'_calls'} );
    $self->{'mock'}->method_2();
    $self->assert_deep_equals( [ 'method_1', 'method_2'  ],
                               $self->{'mock'}{'_calls'} );
    $self->{'mock'}->method_1();
    $self->assert_deep_equals( [ 'method_1', 'method_2', 'method_1'  ],
                               $self->{'mock'}->get_calls() );
}

sub test_set_result {
    my $self = $_[0];

    $self->assert_dies( qr/no method 'method_3' defined in FakePackage/,
                        sub { $self->{'mock'}->set_result( 'method_3' => undef ) } );
    $self->{'mock'}->set_result( 'method_1' => 1 );
    $self->assert_num_equals( 1, $self->{'mock'}{'_results'}{'method_1'} );
    $self->assert_num_equals( 1, $self->{'mock'}->method_1() );
    my $result = $self->{'mock'}->method_2();
    $self->assert_null( $result, 'method_2() != undef');

    my $sub = sub { return join ",", @_ };
    $self->{'mock'}->set_result( 'method_1' => 3,
                                 'method_2' => $sub );
    $self->assert_num_equals( 3, $self->{'mock'}{'_results'}{'method_1'} );
    $self->assert_str_equals( $sub, $self->{'mock'}{'_results'}{'method_2'} );
    $self->assert_num_equals( 3, $self->{'mock'}->method_1() );
    $self->assert_str_equals( join( ",", $self->{'mock'}, 3),
                              $self->{'mock'}->method_2( 3 ) );
}

sub test_invocation_count {
    my $self = $_[0];

    $self->assert_dies( qr/no method 'method_3' defined in FakePackage/,
                        sub { $self->{'mock'}->get_invocation( 'method_3' ) });
    $self->assert_num_equals( 0,
                              $self->{'mock'}->invocation_count( 'method_1' ));
    $self->{'mock'}->method_1();
    $self->{'mock'}->method_1();
    $self->assert_num_equals( 2,
                              $self->{'mock'}->invocation_count( 'method_1' ));
}

sub test_get_invocation {
    my $self = $_[0];

    $self->assert_dies( qr/no method 'method_3' defined in FakePackage/,
                        sub { $self->{'mock'}->get_invocation( 'method_3' ) });
    $self->assert_dies( qr/no invocation 0 for method 'method_1' /,
                        sub { $self->{'mock'}->get_invocation( 'method_1' ) });
    $self->{'mock'}->method_1();
    $self->assert_deep_equals( [ [ $self->{'mock'} ] ],
                               $self->{'mock'}{'_invocations'}{'method_1'} );
    $self->{'mock'}->method_2( 3);
    $self->{'mock'}->method_2( 1, 2 );
    $self->assert_deep_equals( [ [ $self->{'mock'}, 3 ],
                                 [ $self->{'mock'}, 1, 2 ],
                               ],
                               $self->{'mock'}{'_invocations'}{'method_2'} );

    $self->assert_deep_equals( [ $self->{'mock'} ],
                               $self->{'mock'}->get_invocation( 'method_1' ) );
    $self->assert_deep_equals( [ $self->{'mock'}, 1, 2 ],
                               $self->{'mock'}->get_invocation( 'method_2', 1)
                             );
}

package BasePackage;

sub new {
    my $class = shift;
    return bless { 'args' => \@_ }, $class;
}

sub method_1 { 'method_1' }

package FakePackage;

use base qw/BasePackage/;

sub method_2 { 'method_2' }

1;


syntax highlighted by Code2HTML, v. 0.9.1