#!perl
#   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
#   file: t/hook.t #
#
#   Copyright © 2015 Van de Bugger
#
#   This file is part of perl-Dist-Zilla-Plugin-Hook.
#
#   perl-Dist-Zilla-Plugin-Hook is free software: you can redistribute it and/or modify it under
#   the terms of the GNU General Public License as published by the Free Software Foundation,
#   either version 3 of the License, or (at your option) any later version.
#
#   perl-Dist-Zilla-Plugin-Hook 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 the GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License along with
#   perl-Dist-Zilla-Plugin-Hook. If not, see <http://www.gnu.org/licenses/>.
#
#   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

use strict;
use warnings;
use lib 't';

use Test::More;
use Test::Routine::Util;
use Test::Deep qw{ re };

plan tests => 15;

my $role  = 'HookTester';
my $abort = 'Dist::Zilla died in construction: Aborting...';

run_tests( '$plugin and $dist are available', $role, {
    ini_body => [
        [ 'Hook::Init', {
            'hook' => [ '$plugin->log( $dist->name );' ],
        } ],
        'GatherDir',
    ],
    exp_messages => [
        '[Hook::Init] Dummy'
    ],
} );

run_tests( '$plugin == $self', $role, {
    ini_body => [
        [ 'Hook::Init', {
            'hook' => [ '$plugin->log( $plugin == $self ? "OK" : "NOT OK" );' ],
        } ],
        'GatherDir',
    ],
    exp_messages => [
        '[Hook::Init] OK',
    ],
} );

run_tests( '$arg defined', $role, {
    #   Check $arg variable is defined if plugin receives an argument.
    ini_body => [
        [ 'Hook::LicenseProvider', {
            'hook' => [
                '$plugin->log( [',
                '    "Copyright (C) %d %s", $arg->{ copyright_year }, $arg->{ copyright_holder }',
                '] );',
            ],
        } ],
        'GatherDir',
    ],
    exp_messages => [
        '[Hook::LicenseProvider] Copyright (C) 2007 John Doe',
    ],
} );

SKIP: {
    skip 'Not yet decided', 1;      # TODO: Should I declare the variable and let it undefined or
                                    #   not declare variable at all? What about warnongs?
    run_tests( '$arg not defined', $role, {
        #   Check $arg variable is not defined if plugin does receives an argument.
        ini_body => [
            [ 'Hook::BeforeBuild', {
                'hook' => [
                    '$plugin->log( $arg );',
                ],
            } ],
            'GatherDir',
        ],
        exp_exception => 'Aborting...',
        exp_messages => [
            '[Hook::BeforeBuild] Global symbol "$arg" requires explicit package name at Hook::BeforeBuild line 1.',
        ],
    } );
};

run_tests( '@_', $role, {
    #   Check code receives arguments in `@_`.
    #   `provide_license` method of `LicenseProvider` role is called with HashRef with two keys:
    #   `copyright_holder` and `copyright_year`. Let us check it.
    ini_body => [
        [ 'Hook::LicenseProvider', {
            'hook' => [
                'my ( $args ) = @_;',
                'my ( $holder, $year ) = map( $args->{ "copyright_$_" }, qw{ holder year } );',
                '$plugin->log( [ "Copyright (C) %d %s", $year, $holder ] );',
            ],
        } ],
        'GatherDir',
    ],
    exp_messages => [
        '[Hook::LicenseProvider] Copyright (C) 2007 John Doe',
    ],
} );

run_tests( '"use strict;" is in effect', $role, {
    ini_body => [
        [ 'Hook::BeforeBuild', {
            'hook' => [
                '$assa = 123;',
            ],
        } ],
        'GatherDir',
    ],
    exp_exception => 'Aborting...',
    exp_messages => [
        '[Hook::BeforeBuild] Global symbol "$assa" requires explicit package name at Hook::BeforeBuild line 1.',
    ],
} );

run_tests( '"use warnings;" is in effect', $role, {
    #   Using undefined variable causes warning in log, but does not break execution.
    ini_body => [
        [ 'Hook::BeforeBuild', {
            'hook' => [
                'my $assa;',
                'my $qwerty = $assa + 1;',
            ],
        } ],
        'GatherDir',
    ],
    exp_messages => [
        '[Hook::BeforeBuild] Use of uninitialized value $assa in addition (+) at Hook::BeforeBuild line 2.',
    ],
} );

run_tests( 'semicolon does work (if not preceeded by space)', $role, {
    ini_body => [
        [ 'Hook::Init', {
            'hook' => [ '$plugin->log( "Assa" ); $plugin->log( "Qwerty" );' ],
        } ],
        'GatherDir',
    ],
    exp_messages => [
        '[Hook::Init] Assa',
        '[Hook::Init] Qwerty',
    ],
} );

my $hook = { hook => [
    '$plugin->log( "hook" );',
    'if ( $plugin->plugin_name eq "Hook::MetaProvider" ) {',
    '    return {};',
    '} else {',
    '    return undef;',
    '};',
] };

run_tests( 'Phases', $role, {
    msg_grepper => sub {
        return $_ =~ m{^\[(?:Phase_(?:Begins|Ends)|Hook::.+?)\] };
    },
    ini_body => [
        'ReportPhase/Phase_Begins',
        [ 'Hook::AfterBuild',            $hook ],
        [ 'Hook::AfterMint',             $hook ],
        [ 'Hook::AfterRelease',          $hook ],
        [ 'Hook::BeforeArchive',         $hook ],
        [ 'Hook::BeforeBuild',           $hook ],
        [ 'Hook::BeforeMint',            $hook ],
        [ 'Hook::BeforeRelease',         $hook ],
        [ 'Hook::FileGatherer',          $hook ],
        [ 'Hook::FileMunger',            $hook ],
        [ 'Hook::FilePruner',            $hook ],
        [ 'Hook::Init',                  $hook ],
        [ 'Hook::InstallTool',           $hook ],
        [ 'Hook::LicenseProvider',       $hook ],
        [ 'Hook::MetaProvider',          $hook ],
        [ 'Hook::NameProvider',          $hook ],   # TODO: Why this is not shown in the log?
        [ 'Hook::ReleaseStatusProvider', $hook ],
        [ 'Hook::VersionProvider',       $hook ],
        'GatherDir',
        'ReportPhase/Phase_Ends',
    ],
    exp_messages => [
        '[Hook::Init] hook',
        '[Phase_Begins] ########## Before Build ##########',
        '[Hook::BeforeBuild] hook',
        '[Phase_Ends] ########## Before Build ##########',
        '[Phase_Begins] ########## Gather Files ##########',
        '[Hook::FileGatherer] hook',
        '[Phase_Ends] ########## Gather Files ##########',
        '[Phase_Begins] ########## Prune Files ##########',
        '[Hook::FilePruner] hook',
        '[Phase_Ends] ########## Prune Files ##########',
        '[Phase_Begins] ########## Provide Version ##########',
        '[Hook::VersionProvider] hook',
        '[Phase_Ends] ########## Provide Version ##########',
        '[Phase_Begins] ########## Munge Files ##########',
        '[Hook::FileMunger] hook',
        '[Phase_Ends] ########## Munge Files ##########',
        '[Phase_Begins] ########## Bundle Config ##########',   # TODO: Support `PluginBundle`?
        '[Phase_Ends] ########## Bundle Config ##########',
        '[Hook::LicenseProvider] hook',                 # ReportPhase does not have such phase
        '[Hook::ReleaseStatusProvider] hook',
        '[Phase_Begins] ########## Metadata ##########',
        '[Hook::MetaProvider] hook',
        '[Phase_Ends] ########## Metadata ##########',
        '[Phase_Begins] ########## Setup Installer ##########',
        '[Hook::InstallTool] hook',
        '[Phase_Ends] ########## Setup Installer ##########',
        '[Phase_Begins] ########## After Build ##########',
        '[Hook::AfterBuild] hook',
        '[Phase_Ends] ########## After Build ##########',
    ],
} );

run_tests( 'die in hook', $role, {
    ini_body => [
        [ 'Hook::Init', {
            'hook' => [
                '#     this is line 1',
                'die "oops"; # line 2',                         # Die is in line 2.
                '#     this is line 3',
            ],
        } ],
        'GatherDir',
    ],
    exp_exception => $abort,
    exp_messages  => [
        re( qr{^\[Hook::Init\] oops at Hook::Init line 2\b} ),  # Verify the line.
    ],
} );

run_tests( '$code is not available', $role, {
    ini_body => [
        [ 'Hook::Init', {
            'hook' => [
                'use strict;',
                '$plugin->log( $code );',
            ],
        } ],
        'GatherDir',
    ],
    exp_exception => $abort,
    exp_messages  => [
        re( qr{^\[Hook::Init\] Global symbol "\$code" requires explicit package name at Hook::Init line 2\b} ),
    ],
} );

run_tests( 'code dies with exception object, not string', $role, {
    ini_body => [
        [ 'Hook::Init', {
            'hook' => [
                'use strict;',
                'package Exception {',
                '    use Moose;',
                '    with "Throwable";',
                '    has message => ( is => "ro" );',
                '    sub string { shift->message };',
                '    use overload q{""} => \\&string;',
                '}',
                'Exception->throw( { message => "Assa" } );',
            ],
        } ],
        'GatherDir',
    ],
    exp_exception => $abort,
    exp_messages  => [
        re( qr{^\[Hook::Init\] Assa\b} ),
    ],
} );

run_tests( 'prologue', $role, {
    ini_body => [
        [ 'Hook/prologue', {
            'hook' => [
                '$self->log( "prologue" );',
            ],
        } ],
        [ 'Hook::Init', {
            'hook' => [
                '$self->log( "init" );',
            ],
        } ],
        'GatherDir',
    ],
    exp_messages  => [
        '[Hook::Init] prologue',
        '[Hook::Init] init',
    ],
} );

run_tests( 'prologue dies', $role, {
    ini_body => [
        [ 'Hook/prologue', {
            'hook' => [
                '$self->log( "prologue" );',
                'die "oops";',
            ],
        } ],
        [ 'Hook::Init', {
            'hook' => [
                '$self->log( "init" );',
            ],
        } ],
        'GatherDir',
    ],
    exp_exception => $abort,
    exp_messages  => [
        '[Hook::Init] prologue',
        re( qr{\[Hook::Init\] oops at prologue line 2\b} ),
    ],
} );

run_tests( 'prologue + body dies', $role, {
    ini_body => [
        [ 'Hook/prologue', {
            'hook' => [
                '$self->log( "prologue" );',
            ],
        } ],
        [ 'Hook::Init', {
            'hook' => [
                '$self->log( "init" );',
                'die "oops";',
            ],
        } ],
        'GatherDir',
    ],
    exp_exception => $abort,
    exp_messages  => [
        '[Hook::Init] prologue',
        '[Hook::Init] init',
        re( qr{\[Hook::Init\] oops at Hook::Init line 2\b} ),
    ],
} );

done_testing;

exit( 0 );

# end of file #
