#!perl
#   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
#   file: t/text-templater.t
#
#   Copyright © 2015 Van de Bugger
#
#   This file is part of perl-Dist-Zilla-Role-TextTemplater.
#
#   perl-Dist-Zilla-Role-TextTemplater 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-Role-TextTemplater 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-Role-TextTemplater. If not, see <http://www.gnu.org/licenses/>.
#
#   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

use strict;
use warnings;
use lib 't';        # Make ErrorLoggerTestPlugin accessible.

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

plan tests => 19;

my $role     = 'TextTemplaterTester';
my $in_ctor  = 'Dist::Zilla died in construction: ';
my $aborting = 'Aborting...';

run_tests( 'nothing to substitute', $role, {
    text     => [ 'assa' ],         #   Nothing to expand.
    exp_text => [ 'assa' ],         #   Output is the same as output
    exp_messages => [],
} );

run_tests( 'simple substitution', $role, {
    text     => [ '2 + 2 = {{ 2 + 2 }}' ],
    exp_text => [ '2 + 2 = 4'           ],
    exp_messages => [],
} );

run_tests( 'var $dist', $role, {
    #   Check variable `$dist` can be used.
    text     => [ 'name = {{ $dist->name }}' ],
    exp_text => [ 'name = Dummy'             ],
    exp_messages => [],
} );

run_tests( 'var $plugin', $role, {
    #   Check variable `$plugin` can be used.
    text     => [ 'generated with {{ $plugin->plugin_name }}' ],
    exp_text => [ 'generated with =TextTemplaterTestPlugin'   ],
    exp_messages => [],
} );

{
    local $MY::assa;            # Avoid warning "Name "MY::assa" used only once".
    $MY::assa = 'qwerty';
    run_tests( 'global var full name', $role, {
        text     => [ '{{ $MY::assa }}' ],
        exp_text => [ 'qwerty'          ],
        exp_messages => [],
    } );
    run_tests( 'global var short name + package', $role, {
        package  => 'MY',
        text     => [ '{{ $assa }}' ],
        exp_text => [ 'qwerty'      ],
        exp_messages => [],
    } );
}

run_tests( '$dist + $plugin + package', $role, {
    package  => 'MY',
    text     => [
        '# Generated with {{ $plugin->plugin_name }}',
        '{{ $dist->name . " " . $dist->version }}',
    ],
    exp_text => [
        '# Generated with =TextTemplaterTestPlugin',
        'Dummy 0.001'
    ],
    exp_messages => [],
} );

run_tests( 'prepend', $role, {
    #   Make sure `prepend` works.
    prepend  => [ 'my $yep = "nope";' ],                # Define a variable in the fragment.
    text     => [ '{{ $yep = "yep"; }}/{{ $yep }}' ],   # Note: Variable is reset to its original
    exp_text => [ 'yep/nope'                       ],   # value in the beginning of each fragment.
    exp_messages => [],
} );

run_tests( 'no closing braces', $role, {
    #   Syntax error in template: no closing braces.
    text          => [ '{{ $assa } }' ],
    exp_exception => $in_ctor . $aborting,
    exp_messages  => [
        'End of data inside program text that began at line 1',
    ]
} );

run_tests( 'die', $role, {
    #   Code fragment dies. Check linenumbers are reported correctly.
    text => [
        # `Dist::Zilla` configuration parser strips leading and trailing spaces!
        'a        template line 1',
        'b        template line 2',
        '{{     # template line 3, code line 1',
        'die;   # template line 4, code line 2',
        '2 + 2; # template line 5, code line 3 }}',
        'c        template line 6'
    ],
    exp_exception => $in_ctor . $aborting,
    exp_messages  => [
        re( qr{^Died at template line 4\b} ),
        'Problem code fragment begins at template line 3.',
        'Template text:',
        '    1: a        template line 1',
        '    2: b        template line 2',
        '    3: {{     # template line 3, code line 1',
        '>>> 4: die;   # template line 4, code line 2',
        '    5: 2 + 2; # template line 5, code line 3 }}',
        '    6: c        template line 6',
    ]
} );

{
    package MY;
    sub oops {
        die;
    };
}

run_tests( 'deep die', $role, {
    #   Program fragment dies, but `die` is buried in the calling code.
    text => [ '{{ MY::oops(); }}' ],
    exp_exception => $in_ctor . $aborting,
    exp_messages  => [
        re( qr{^Died at t/text-templater\.t line \d+\b} ),
        'Problem code fragment begins at template line 1.',
        'Template text:',
        '    1: {{ MY::oops(); }}',
    ]
} );

run_tests( 'undefined subroutine', $role, {
    #   Call undefined subroutine. It is fatal error.
    text => [ '{{ MY::undefined(); }}' ],
    exp_exception => $in_ctor . $aborting,
    exp_messages  => [
        re( qr{^Undefined subroutine &MY::undefined called at template line 1\b} ),
        'Problem code fragment begins at template line 1.',
        'Template text:',
        '>>> 1: {{ MY::undefined(); }}',
    ]
} );

run_tests( 'prepend + warnings', $role, {
    #   Use undefined variable. It causes warning, wich should appear in the log, but build should
    #   complete successfuly.
    prepend  => [ 'use warnings;' ],
    text     => [ '{{ $MY::assa + 2 }}' ],
    exp_text => [ '2' ],
    exp_messages  => [
        re( qr{^Use of uninitialized value \$MY::assa in addition \(\+\) at template line 1\b} ),
    ]
} );

{
    local $TextTemplaterTestPlugin::Hook = sub {
        my ( $self, $string ) = @_;
        $self->fill_in_string( $string, { assa => 'Yahoo' } );
    };
    run_tests( 'custom variables', $role, {
        text         => [ '{{ $assa }}' ],
        exp_messages => [],
        exp_text     => [ 'Yahoo' ],
    } );
}

{
    #   Explicitly specified $plugin and $dist overrides default definition.
    local $TextTemplaterTestPlugin::Hook = sub {
        my ( $self, $string ) = @_;
        $self->fill_in_string( $string, { plugin => 'Mooo', dist => 'Yep' } );
    };
    run_tests( 'explicit $plugin and $dist', $role, {
        text         => [ '{{ $plugin . " " . $dist }}' ],
        exp_text     => [ 'Mooo Yep' ],
        exp_messages => [],
    } );
}

{
    local ( $MY::assa, $ME::assa );
    $MY::assa = 'qwerty';
    $ME::assa = 'ytrewq';
    #   Explicitly specified lowercase option overrides the attribute.
    {
        local $TextTemplaterTestPlugin::Hook = sub {
            my ( $self, $string ) = @_;
            $self->fill_in_string( $string, undef, { package => 'ME' } );
        };
        run_tests( 'explicit $plugin and $dist', $role, {
            text         => [ '{{ $assa }}' ],
            package      => 'MY',
            exp_text     => [ 'ytrewq' ],
            exp_messages => [],
        } );
    }
    #   Explicitly specified uppercase option does not override the attribute.
    {
        local $TextTemplaterTestPlugin::Hook = sub {
            my ( $self, $string ) = @_;
            $self->fill_in_string( $string, undef, { PACKAGE => 'ME' } );
        };
        run_tests( 'explicit $plugin and $dist', $role, {
            text         => [ '{{ $assa }}' ],
            package      => 'MY',
            exp_text     => [ 'qwerty' ],
            exp_messages => [],
        } );
    }
}

run_tests( 'non-standard delimiters', $role, {
    delimiters    => '(* *)',
    text          => [ '(* 3 * 3 *)' ],
    exp_text      => [ '9' ],
    exp_messages  => [],
} );

run_tests( 'bad delimiters', $role, {
    delimiters    => '(**)',
    text          => [ '(* 3 * 3 *)' ],
    exp_exception => re( qr{^$in_ctor"delimiters" value must be Str of \*two\* whitespace-separated words} ),
    exp_messages  => [],
} );

done_testing;

exit( 0 );

# end of file #
