# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package Apache::TestMM;

use strict;
use warnings FATAL => 'all';

use Config;
use Apache::TestConfig ();
use Apache::TestTrace;

sub import {
    my $class = shift;

    for my $section (@_) {
        unless (defined &$section) {
            die "unknown Apache::TestMM section: $section";
        }
        no strict 'refs';
        my $sub = "MY::$section";
        # Force aliasing, since previous WriteMakefile might have
        # moved it
        undef &$sub if defined &$sub;
        *$sub = \&{$section};
    }
}

sub add_dep {
    my($string, $targ, $add) = @_;
    $$string =~ s/($targ\s+::)/$1 $add /;
}

sub clean {
    my $self = shift;
    my $string = $self->MM::clean(@_);
    add_dep(\$string, clean => 'test_clean');
    $string;
}

sub test {
    my $self = shift;
    my $env = Apache::TestConfig->passenv_makestr();

    my $tests = "TEST_FILES =\n";

    if (ref $self && exists $self->{'test'}) {
        $tests = 'TEST_FILES = ' . $self->{'test'}->{'TESTS'} . "\n";
    }

    my $preamble = Apache::TestConfig::WIN32 ? "" : <<EOF;
PASSENV = $env
EOF

    my $cover;

    if (eval { require Devel::Cover }) {
        my $atdir = File::Spec->catfile($ENV{HOME}, '.apache-test');

        my $cover_exec = Apache::TestConfig::which("cover");

        my @cover = ("", "testcover :", );
        push @cover, "\t-\@$cover_exec -delete" if $cover_exec;
        push @cover, "\t-HARNESS_PERL_SWITCHES=-MDevel::Cover=+inc,$atdir \\",
            "\tAPACHE_TEST_EXTRA_ARGS=-one-process \$(MAKE) test";
        push @cover, "\t-\@$cover_exec" if $cover_exec;
        $cover = join "\n", @cover, "";
    }
    else {

        $cover = <<'EOF';

testcover :
	@echo "Cannot run testcover action unless Devel::Cover is installed"
	@echo "Don't forget to rebuild your Makefile after installing Devel::Cover"
EOF
    }

    return $preamble . $tests . <<'EOF' . $cover;
TEST_VERBOSE = 0

test_clean :
	$(FULLPERL) -I$(INST_ARCHLIB) -I$(INST_LIB) \
	t/TEST $(APACHE_TEST_EXTRA_ARGS) -clean

run_tests :
	$(PASSENV) \
	$(FULLPERL) -I$(INST_ARCHLIB) -I$(INST_LIB) \
	t/TEST $(APACHE_TEST_EXTRA_ARGS) -bugreport -verbose=$(TEST_VERBOSE) $(TEST_FILES)

test :: pure_all test_clean run_tests

test_config :
	$(PASSENV) \
	$(FULLPERL) -I$(INST_ARCHLIB) -I$(INST_LIB) \
	t/TEST $(APACHE_TEST_EXTRA_ARGS) -conf

cmodules: test_config
	cd c-modules && $(MAKE) all

cmodules_clean: test_config
	cd c-modules && $(MAKE) clean
EOF

}

sub generate_script {
    my $file = shift;

    unlink $file if -e $file;

    my $body = "BEGIN { eval { require blib && blib->import; } }\n";

    my %args = @Apache::TestMM::Argv;
    while (my($k, $v) = each %args) {
        $v =~ s/\|/\\|/g;
        $body .= "\n\$Apache::TestConfig::Argv{'$k'} = q|$v|;\n";
    }

    my $in = Symbol::gensym();
    open $in, "$file.PL" or die "Couldn't open $file.PL: $!";
    {
        local $/;
        $body .= <$in>;
    }
    close $in;

    info "generating script $file";
    Apache::Test::basic_config()->write_perlscript($file, $body);
}

sub filter_args {
    my($argv, $vars) =
        Apache::TestConfig::filter_args(\@ARGV, \%Apache::TestConfig::Usage);
    @ARGV = @$argv;
    @Apache::TestMM::Argv = %$vars;
}

1;

=head1 NAME

Apache::TestMM - Provide MakeMaker Wrapper Methods

=head1 SYNOPSIS

  require Apache::TestMM;
  
  # import MY::test and MY::clean overrides for MM
  Apache::TestMM->import(qw(test clean));
  
  # parse command line args
  Apache::TestMM::filter_args();
  
  # autogenerate the script
  Apache::TestMM::generate_script('t/TEST');

=head1 DESCRIPTION

C<Apache::TestMM> provides wrappers for the C<ExtUtils::MakeMaker>
craft, making it easier to extend the autogenerated F<Makefile> with
C<Apache::Test>.

=head1 FUNCTIONS

=head2 C<import>

  use Apache::TestMM qw(test clean);

or:

  Apache::TestMM->import(qw(test clean));

Imports C<MY::> overrides for the default C<ExtUtils::MakeMaker>
I<test> and I<clean> targets, as if you have defined:

  sub MY::test {...}
  sub MY::clean {...}

in F<Makefile.PL>. C<Apache::TestMM> does this for you so that these Makefile
targets will run the Apache server and the tests for it, and clean up after
its mess.

=head2 C<filter_args>

  push @ARGV, '-apxs', $apxs_path;
  Apache::TestMM::filter_args();
  WriteMakefile(...);

When C<WriteMakefile()> is called it parses C<@ARGV>, hoping to find
special options like C<PREFIX=/home/stas/perl>. C<Apache::Test>
accepts a lot of configuration options of its own. When
C<Apache::TestMM::filter_args()> is called, it removes any
C<Apache::Test>-specific options from C<@ARGV> and stores them
internally, so when C<WriteMakefile()> is called they aren't in
C<@ARGV> and thus won't be processed by C<WriteMakefile()>.

The options can be set when F<Makefile.PL> is called:

  % perl Makefile.PL -apxs /path/to/apxs

Or you can push them manually to C<@ARGV> from the code:

  push @ARGV, '-apxs', $apxs_path;

When:

  Apache::TestMM::generate_script('t/TEST');

is called, C<Apache::Test>-specific options extracted by
C<Apache::TestMM::filter_args()> are written to the autogenerated
file. In our example, the autogenerated F<t/TEST> will include:

  %Apache::TestConfig::Argv = qw(apxs /path/to/apxs);

which is going to be used by the C<Apache::Test> runtime.

The other frequently used options are: C<-httpd>, telling where to
find the httpd (usually when the C<-apxs> option is not used),
C<-libmodperl> to use a specific mod_perl shared object (if your
mod_perl is built as DSO), C<-maxclients> to change the default number
of the configured C<MaxClients> directive, C<-port> to start the
server on a specific port, etc. To get the complete list of available
configuration options and their purpose and syntax, run:

  % perl -MApache::TestConfig -le 'Apache::TestConfig::usage()'

You may wish to document some of these in your application's F<README>
file, especially the C<-apxs> and C<-httpd> options.


=head2 C<generate_script>

  Apache::TestMM::generate_script('t/TEST');

C<generate_script()> accepts the name of the script to generate and
will look for a template with the same name and suffix I<.PL>. So in
our example it'll look for F<t/TEST.PL>. The autogenerated script
F<t/TEST> will include the contents of F<t/TEST.PL>, and special
directives, including any configuration options passed via
C<L<filter_args()|/C_filter_args_>> called from F<Makefile.PL>, special
fixup code, etc.

=cut
