—# 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
#
#
# 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::TestMB;
use
strict;
use
Module::Build 0.18;
use
Apache::Test ();
use
Apache::TestConfig ();
@ISA
=
qw(Module::Build)
;
sub
new {
my
$pkg
=
shift
;
my
(
$argv
,
$vars
) =
Apache::TestConfig::filter_args(\
@ARGV
, \
%Apache::TestConfig::Usage
);
@ARGV
=
@$argv
;
my
$self
=
$pkg
->SUPER::new(
@_
);
$self
->{properties}{apache_test_args} =
$vars
;
$self
->{properties}{apache_test_script} ||=
't/TEST'
;
$self
->generate_script;
return
$self
;
}
sub
valid_property {
return
1
if
defined
$_
[1] &&
(
$_
[1] eq
'apache_test_args'
||
$_
[1] eq
'apache_test_script'
);
shift
->SUPER::valid_property(
@_
);
}
sub
apache_test_args {
my
$self
=
shift
;
$self
->{properties}{apache_test_args} =
shift
if
@_
;
return
$self
->{properties}{apache_test_args};
}
sub
apache_test_script {
my
$self
=
shift
;
$self
->{properties}{apache_test_script} =
shift
if
@_
;
return
$self
->{properties}{apache_test_script};
}
sub
ACTION_test_clean {
my
$self
=
shift
;
# XXX I'd love to do this without t/TEST.
$self
->do_system(
$self
->perl,
$self
->_bliblib,
$self
->localize_file_path(
$self
->apache_test_script),
'-clean'
);
}
sub
ACTION_clean {
my
$self
=
shift
;
$self
->depends_on(
'test_clean'
);
$self
->SUPER::ACTION_clean(
@_
);
}
sub
ACTION_run_tests {
my
$self
=
shift
;
$self
->depends_on(
'test_clean'
);
# XXX I'd love to do this without t/TEST.
$self
->do_system(
$self
->perl,
$self
->_bliblib,
$self
->localize_file_path(
$self
->apache_test_script),
'-bugreport'
,
'-verbose='
. (
$self
->verbose || 0));
}
sub
ACTION_testcover {
my
$self
=
shift
;
unless
(
$self
->find_module_by_name(
'Devel::Cover'
, \
@INC
)) {
warn
(
"Cannot run testcover action unless Devel::Cover "
.
"is installed.\n"
.
"Don't forget to rebuild your Makefile after "
.
"installing Devel::Cover\n"
);
return
;
}
$self
->add_to_cleanup(
'coverage'
,
'cover_db'
);
my
$atdir
=
$self
->localize_file_path(
"$ENV{HOME}/.apache-test"
);
local
$Test::Harness::switches
=
local
$Test::Harness::Switches
=
local
$ENV
{HARNESS_PERL_SWITCHES} =
"-MDevel::Cover=+inc,'$atdir'"
;
local
$ENV
{APACHE_TEST_EXTRA_ARGS} =
"-one-process"
;
$self
->depends_on(
'test'
);
$self
->do_system(
'cover'
);
}
sub
ACTION_test_config {
my
$self
=
shift
;
$self
->do_system(
$self
->perl,
$self
->_bliblib,
$self
->localize_file_path(
$self
->apache_test_script),
'-conf'
,
'-verbose='
. (
$self
->verbose || 0));
}
sub
_bliblib {
my
$self
=
shift
;
return
(
'-I'
, File::Spec->catdir(
$self
->base_dir,
$self
->blib,
'lib'
),
'-I'
, File::Spec->catdir(
$self
->base_dir,
$self
->blib,
'arch'
),
);
}
sub
ACTION_test {
my
$self
=
shift
;
$self
->depends_on(
'code'
);
$self
->depends_on(
'run_tests'
);
$self
->depends_on(
'test_clean'
);
}
sub
_cmodules {
my
(
$self
,
$action
) =
@_
;
die
"The cmodules"
. (
$action
ne
'all'
?
"_$action"
:
''
)
.
" action is not yet implemented"
;
# XXX TBD.
$self
->depends_on(
'test_config'
);
my
$start_dir
=
$self
->cwd;
chdir
$self
->localize_file_path(
'c-modules'
);
# XXX How do we get Build.PL to be generated instead of Makefile?
# Subclass Apache::TestConfigC, perhaps?
$self
->do_system(
'Build.PL'
,
$action
);
chdir
$start_dir
;
}
sub
ACTION_cmodules {
shift
->_cmodues(
'all'
) }
sub
ACTION_cmodules_clean {
shift
->_cmodues(
'clean'
) }
# XXX I'd love to make this optional.
sub
generate_script {
my
$self
=
shift
;
# If a file name has been passed in, use it. Otherwise, use the
# one set up when the Apache::TestMB object was created.
my
$script
=
$self
->localize_file_path(
$_
[0]
?
$self
->apache_test_script(
shift
)
:
$self
->apache_test_script
);
# We need a class to run the tests from t/TEST.
my
$class
=
pop
||
'Apache::TestRunPerl'
;
# Delete any existing instance of the file.
unlink
$script
if
-e
$script
;
# Start the contents of t/TEST.
my
$body
=
"BEGIN { eval { require blib && blib->import; } }\n"
;
# Configure the arguments for t/TEST.
while
(
my
(
$k
,
$v
) =
each
%{
$self
->apache_test_args }) {
$v
=~ s/\|/\\|/g;
$body
.=
"\n\$Apache::TestConfig::Argv{'$k'} = q|$v|;\n"
;
}
my
$infile
=
"$script.PL"
;
if
(-f
$infile
) {
# Use the existing t/TEST.PL.
my
$in
= Symbol::gensym();
open
$in
,
"$infile"
or
die
"Couldn't open $infile: $!"
;
local
$/;
$body
.= <
$in
>;
close
$in
;
}
else
{
# Create t/TEST from scratch.
$body
.=
join
"\n"
,
Apache::TestConfig->perlscript_header,
"use $class ();"
,
"$class->new->run(\@ARGV);"
;
}
# Make it so!
"Generating test running script $script\n"
if
$self
->verbose;
Apache::Test::basic_config()->write_perlscript(
$script
,
$body
);
$self
->add_to_cleanup(
$self
->apache_test_script);
}
1;
__END__
=head1 NAME
Apache::TestMB - Subclass of Module::Build to support Apache::Test
=head1 SYNOPSIS
Standard process for building & installing modules:
perl Build.PL
./Build
./Build test
./Build install
Or, if you're on a platform (like DOS or Windows) that doesn't like the "./"
notation, you can do this:
perl Build.PL
perl Build
perl Build test
perl Build install
=head1 DESCRIPTION
This class subclasses C<Module::Build> to add support for testing
Apache integration with Apache::Test. It is broadly based on
C<Apache::TestMM>, and as such adds a number of build actions to a the
F<Build> script, while simplifying the process of creating F<Build.PL>
scripts.
Here's how to use C<Apache::TestMB> in a F<Build.PL> script:
use Module::Build;
my $build_pkg = eval { require Apache::TestMB }
? 'Apache::TestMB' : 'Module::Build';
my $build = $build_pkg->new(
module_name => 'My::Module',
);
$build->create_build_script;
This is identical to how C<Module::Build> is used. Not all target
systems may have C<Apache::Test> (and therefore C<Apache::TestMB>
installed, so we test for it to be installed, first. But otherwise,
its use can be exactly the same. Consult the
L<Module::Build|Module::Build> documentation for more information on
how to use it; L<Module::Build::Cookbook|Module::Build::Cookbook> may
be especially useful for those looking to migrate from
C<ExtUtils::MakeMaker>.
=head1 INTERFACE
=head2 Build
With the above script, users can build your module in the usual
C<Module::Build> way:
perl Build.PL
./Build
./Build test
./Build install
If C<Apache::TestMB> is installed, then Apache will be started before
tests are run by the C<test> action, and shut down when the tests
complete. Note that C<Build.PL> can be called C<Apache::Test>-specific
options in addition to the usual C<Module::Build> options. For
example:
perl Build.PL -apxs /usr/local/apache/bin/apxs
Consult the L<Apache::Test|Apache::Test> documentation for a complete
list of options.
In addition to the actions provided by C<Module::Build> (C<build>,
C<clean>, C<code>, C<test>, etc.), C<Apache::TestMB> adds a few extra
actions:
=over 4
=item test_clean
This action cleans out the files generated by the test script,
F<t/TEST>. It is also executed by the C<clean> action.
=item run_tests
This action actually the tests by executing the test script,
F<t/TEST>. It is executed by the C<test> action, so most of the time
it won't be executed directly.
=item testcover
C<Apache::TestMB> overrides this action from C<Module::Build> in order to
prevent the C<Apache::Test> preference files from being included in the test
coverage.
=back
=head2 Constructor
=head3 new
The C<new()> constructor takes all the same arguments as its parent in
C<Module::Build>, but can optionally accept one other parameter:
=over
=item apache_test_script
The name of the C<Apache::Test> test script. The default value is
F<t/TEST>, which will work in the vast majority of cases. If you wish
to specify your own file name, do so with a relative file name using
Unix-style paths; the file name will automatically be converted for
the local platform.
=back
When C<new()> is called it does the following:
=over 4
=item *
Processes the C<Apache::Test>-specific options in C<@ARGV>. See the
L<Apache::Test|Apache::Test> documentation for a complete list of
options.
=item *
Sets the name of the C<Apache::Test> test script to F<t/TEST>, unless
it was explicitly specified by the C<apache_test_script> parameter.
=item *
Calls C<generate_script()> to generate C<Apache::Test> test script,
usually F<t/TEST>.
=back
=head2 Instance Methods
=head3 apache_test_args
Returns a hash reference containing all of the settings specified by
options passed to F<Build.PL>, or explicitly added to C<@ARGV> in
F<Build.PL>. Consult the L<Apache::Test|Apache::Test> documentation
for a complete list of options.
=head3 apache_test_script
Gets or sets the file name of the C<Apache::Test> test script.
=head3 generate_script
$build->generate_script;
$build->generate_script('t/FOO');
$build->generate_script(undef, 'Apache::TestRun');
This method is called by C<new()>, so in most cases it can be
ignored. If you'd like it to use other than the default arguments, you
can call it explicitly in F<Build.PL> and pass it the arguments you
desire. It takes two optional arguments:
=over 4
=item *
The name of the C<Apache::Test> test script. Defaults to the value
returned by C<apache_test_script()>.
=item *
The name of an C<Apache::Test> test running class. Defaults to
C<Apache::TestRunPerl>.
=back
If there is an existing F<t/TEST.PL> (or a script with the same name
as specified by the C<apache_test_script> parameter but with F<.PL>
appended to it), then that script will be used as the template for the
test script. Otherwise, a simple test script will be written similar
to what would be written by C<Apache::TestRun::generate_script()>
(although that function is not aware of the arguments passed to
F<Build.PL>, so use this one instead!).
=head1 SEE ALSO
=over 4
=item L<Apache::TestRequest|Apache::TestRequest>
Demonstrates how to write tests to send requests to the Apache server
run by C<./Build test>.
=item L<Module::Build|Module::Build>
The parent class for C<Apache::TestMB>; consult it's documentation for
more on its interface.
This article by Geoffrey Young explains how to configure Apache and
write tests for your module using Apache::Test. Just use
C<Apache::TestMB> instead of C<Apache::TestMM> to update it for use
with C<Module::Build>.
=back
=head1 AUTHOR
David Wheeler
Questions can be asked at the test-dev <at> httpd.apache.org list. For
more information see: I<http://httpd.apache.org/test/> and
=cut