#!/usr/bin/perl
use warnings;
use strict;

use Carp qw(croak);
use Getopt::Long qw(:config no_ignore_case pass_through);
use File::Find;
use Module::Load;
use Test::BrewBuild;

our $VERSION = '1.03_02';

my $log = Test::BrewBuild->new()->log()->child('brewbuild');
my (%args, $help);

GetOptions(
    "on=s@"         => \$args{on},
    "new=i"         => \$args{new},
    "r|remove"      => \$args{remove},
    "R|revdep"      => \$args{revdep},
    "plugin=s"      => \$args{plugin},
    "args=s@"       => \$args{args},
    "debug=i"       => \$args{debug},
    "version=s@"    => \$args{version},
    "help"          => \$help,
);

if ($help){
    print <<EOF;

Usage: perl build/brewbuild.pl [options]

Options:

--on     | -o:  String, perl version number to run against (can be supplied multiple times
--plugin | -p:  String, the module name of the exec command plugin to use
--args   | -a:  List of args to pass into the plugin (one arg per loop)
--revdep | -R   Run tests, install, then run tests on all CPAN reverse dependency modules
--debug  | -d:  Int (0-7), sets logging verbosity
--new    | -n:  Integer, how many random versions of perl to install
--remove | -r:  Bool, remove all installed perls (less the current one) before installation of new ones
--verion | -v:  String, the number portion of an available perl version according to "*brew available". Multiple versions can besent in at once
--help   | -h:  print this help message
EOF
exit;
}

if (! -d 't/'){
    croak "\nthere's no 't/' directory, nothing to test. Exiting...\n";
}

my $is_win = $^O =~ /MSWin/ ? 1 : 0;
my $brew_prog = $is_win ? 'berrybrew.exe' : 'perlbrew';
my $brew_link = $is_win 
    ? 'https://github.com/dnmfarrell/berrybrew'
    : 'http://perlbrew.pl';
my $sep = $is_win ? ';' : ':';
my @modules;

if (! grep { -x "$_/$brew_prog"}split /$sep/,$ENV{PATH}){
    warn "\nYou need to install '$brew_prog' to use this program...\n\n" .
         "See $brew_link\n\n";
    exit;
}

if ($args{revdep}){

    $log->_6('running --revdep');

    delete $args{args};
    load 'CPAN::ReverseDependencies';

    find({ wanted => \&_module_find, no_chdir => 1}, 'lib/');
    my $mod = $modules[0];

    $log->_7("using '$mod' as the project we're working on");

    $mod =~ s|lib/||;
    $mod =~ s|/|-|g;
    $mod =~ s|\.pm||;

    $log->_7("working module translated to $mod");

    my $rvdep = CPAN::ReverseDependencies->new;
    my @revdeps = $rvdep->get_reverse_dependencies($mod);

    $log->_0("working on reverse dependencies: " . join ', ', @revdeps);

    $args{plugin} = 'Test::BrewBuild::Plugin::TestAgainst';

    for (@revdeps){
        s/-/::/g;
        $args{plugin_arg} = $_;
        _exec_brew(%args);
    }
}
elsif (defined $args{args}){
    my %opts = %args;

    $log->_7("running a plugin with args");

    delete $args{args};

    for (@{ $opts{args} }) {
        $args{plugin_arg} = $_;
        _exec_brew( %args );
    }

    delete $args{new};
    delete $args{remove};
}
else {
    $log->_7("default run");
    _exec_brew(%args);
}
sub _exec_brew {
    my $brew = Test::BrewBuild->new(%args);

    $log = $log->child('_exec_brew');

    my @opts;
    for (keys %args){
        push @opts, "$_ => $args{$_}" if defined $args{$_};
    }

    $log->_6("executing Test::BrewBuild run() with args: " . join ', ', @opts);

    $brew->run;
}
sub _module_find {
    $log = $log->child('_module_find');
    $log->_7("finding modules");
    push @modules, $_ if -f $_;
}
=pod

=head1 NAME

brewbuild - This is the front-end script for L<Test::BrewBuild>

=for html
<a href="http://travis-ci.org/stevieb9/p5-test-brewbuild"><img src="https://secure.travis-ci.org/stevieb9/p5-test-brewbuild.png"/>
<a href='https://coveralls.io/github/stevieb9/p5-test-brewbuild?branch=master'><img src='https://coveralls.io/repos/stevieb9/p5-test-brewbuild/badge.svg?branch=master&service=github' alt='Coverage Status' /></a>

=head1 SYNOPSIS

You must be in the root directory of the distribution you want to test. Note
that all arguments passed into the script have single-letter counterparts. Also
note that each time the command is run, your unit tests will be run on all
installed *brew instances.

Run all unit tests against all installed instances with no other action

    brewbuild

    # output

    perl-5.8.9 :: PASS
    perl-5.20.3 :: PASS
    perl-5.22.1 :: PASS

Print usage information

    brewbuild -h

Run on specific versions only

    brewbuild --on 5.20.3 -o 5.8.9

Run tests on the local working copy of the current module, then run all tests
of all reverse dependencies of this module (as reported by CPAN), to ensure
the down river modules will work with your new build

    brewbuild --revdep # or -R

Install three new instances of perl, randomly

    brewbuild --new 3

Enable verbose output. '5' displays typical output. '7' is full debug

    brewbuild --debug 5

Remove all perl instances (less the currently used one), install two new random
versions, and run tests against all installed perls

    brewbuild --remove --new 2

Install all available perl versions, and run tests against all of them

    brewbuild --new -1

Install a specific version and run tests on all instances (include just the
number portion of the version per "perlbrew available" or "berrybrew available"

    brewbuild --version 5.20.3

...multiple versions can be passed in at once

    brewbuild -v 5.20.3 -v 5.14.4 -v 5.23.5

Use a custom plugin containing the build/test commands to pass to 'perlbrew
exec' (can also be set in $ENV{TBB_PLUGIN} for persistence).

The plugin arg will take a filename as well if the module isn't installed. Send
in the relative/full path if the plugin isn't in the current working directory.

Your 'brewbuild_exec()' function will receive a L<Logging::Simple> object,
followed by a single argument if specified per below

    brewbuild --plugin 'Test::BrewBuild::Plugin::DefaultExec' # default

Send in args that custom plugins can use. brewbuild will be called once for
each arg, passing a single argument from the list specified on each run. Note:
--new and --remove are only executed on the first loop

    brewbuild -p My::Plugin --args 1 -a 2

=head1 DESCRIPTION

This C<brewbuild> script installed by the L<Test::Brewbuild> module allows you
to perform your unit tests across all of your Perlbrew (Unix) or Berrybrew
(Windows) Perl instances, with the ability to very easily create your own
plugins that contain the code that C<perlbrew/berrybrew exec> will process.
(See L<Test::BrewBuild::Plugin::DefaultExec> for an example).

For Windows, you'll need to install Berrybrew (see L<SEE ALSO> for details).
For Unix, you'll need Perlbrew.

It allows you to remove and reinstall on each test run, install random versions
of perl, or install specific versions.

All unit tests are run against all installed instances.

The actual module is just a helper for the installed script, and isn't designed
for end-user use.

=head1 AUTHOR

Steve Bertrand, C<< <steveb at cpan.org> >>

=head2 CONTRIBUTING

Any and all feedback and help is appreciated. A Pull Request is the preferred
method of receiving changes (L<https://github.com/stevieb9/p5-test-brewbuild>),
but regular patches through the bug tracker, or even just email discussions are
welcomed.

=head1 BUGS

L<https://github.com/stevieb9/p5-test-brewbuild/issues>

=head1 SUPPORT

You can find documentation for this script and module with the perldoc command.

    perldoc brewbuild
    perldoc Test::BrewBuild

=head1 SEE ALSO

Berrybrew for Windows:

L<https://github.com/dnmfarrell/berrybrew>

Perlbrew for Unixes:

L<http://perlbrew.pl>

=head1 LICENSE AND COPYRIGHT

Copyright 2016 Steve Bertrand.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See L<http://dev.perl.org/licenses/> for more information.
