1
0
Fork 0

Adding debian version 2.4.63-1.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
Daniel Baumann 2025-06-22 11:01:27 +02:00
parent 7263481e48
commit f56986e2d9
Signed by: daniel.baumann
GPG key ID: BCC918A2ABD66424
1490 changed files with 80785 additions and 0 deletions

View file

@ -0,0 +1,21 @@
=head1 NAME
Changes - Apache::TestMe changes logfile
=head1 Changes
=over 4
=item 0.01
new test basic/vhost.t which introduces a vhost entry in .pm. also
added a dummy vhost entry in t/conf/extra.conf.in, the setup needed by
t/minmaxclients.t from Apache-TestItSelf [Stas]
write a basic mod_perl test: basic/hello.t [Stas]
starting the config test suite used by Apache::TestItSelf [Stas]
=back
=cut

View file

@ -0,0 +1,221 @@
use 5.005;
use lib qw(../../lib); # Apache-Test/lib
use Apache::TestMM qw(test clean);
use Apache::TestMM ();
use Apache::TestReport;
use ExtUtils::MakeMaker ();
my $mp_gen = satisfy_mp_generation();
warn "Goind to build against mod_perl/$mod_perl::VERSION Perl/$]\n";
Apache::TestMM::filter_args();
my @scripts = qw(t/TEST);
for (@scripts) {
Apache::TestMM::generate_script($_);
}
Apache::TestReport->generate_script;
my @clean_files = (@scripts, qw(t/REPORT));
my %common_opts = (
NAME => 'Apache-TestMe',
VERSION => '0.01',
clean => {
FILES => "@clean_files",
},
);
if ($mp_gen == 1) {
require ExtUtils::MakeMaker;
ExtUtils::MakeMaker::WriteMakefile(
%common_opts,
);
}
else {
require ModPerl::MM;
ModPerl::MM::WriteMakefile(
%common_opts,
);
}
# If a specific generation was passed as an argument,
# if satisfied
# return the same generation
# else
# die
# else @ARGV and %ENV will be checked for specific orders
# if the specification will be found
# if satisfied
# return the specified generation
# else
# die
# else if any mp generation is found
# return it
# else
# die
sub satisfy_mp_generation {
my $wanted = shift || wanted_mp_generation();
unless ($wanted == 1 || $wanted == 2) {
die "don't know anything about mod_perl generation: $wanted\n" .
"currently supporting only generations 1 and 2";
}
my $selected = 0;
if ($wanted == 1) {
require_mod_perl();
if ($mod_perl::VERSION >= 1.99) {
# so we don't pick 2.0 version if 1.0 is wanted
die "You don't seem to have mod_perl 1.0 installed";
}
$selected = 1;
}
elsif ($wanted == 2) {
#warn "Looking for mod_perl 2.0";
require_mod_perl2();
if ($mod_perl::VERSION < 1.99) {
die "You don't seem to have mod_perl 2.0 installed";
}
$selected = 2;
}
else {
$selected = eval { require_mod_perl2() or require_mod_perl() };
warn "Using $mod_perl::VERSION\n";
}
return $selected;
}
sub require_mod_perl {
eval { require mod_perl };
die "Can't find mod_perl installed\nThe error was: $@" if $@;
1;
}
sub require_mod_perl2 {
eval { require mod_perl2 };
die "Can't find mod_perl installed\nThe error was: $@" if $@;
2;
}
# the function looks at %ENV and Makefile.PL option to figure out
# whether a specific mod_perl generation was requested.
# It uses the following logic:
# via options:
# perl Makefile.PL MOD_PERL=2
# or via %ENV:
# env MOD_PERL=1 perl Makefile.PL
#
# return value is:
# 1 or 2 if the specification was found (mp 1 and mp 2 respectively)
# 0 otherwise
sub wanted_mp_generation {
# check if we have a command line specification
# flag: 0: unknown, 1: mp1, 2: mp2
my $flag = 0;
my @pass;
while (@ARGV) {
my $key = shift @ARGV;
if ($key =~ /^MOD_PERL=(\d)$/) {
$flag = $1;
}
else {
push @pass, $key;
}
}
@ARGV = @pass;
# check %ENV
my $env = exists $ENV{MOD_PERL} ? $ENV{MOD_PERL} : 0;
# check for contradicting requirements
if ($env && $flag && $flag != $env) {
die <<EOF;
Can\'t decide which mod_perl version should be used, since you have
supplied contradicting requirements:
enviroment variable MOD_PERL=$env
Makefile.PL option MOD_PERL=$flag
EOF
}
my $wanted = 0;
$wanted = 2 if $env == 2 || $flag == 2;
$wanted = 1 if $env == 1 || $flag == 1;
unless ($wanted) {
# if still unknown try to require mod_perl.pm
eval { require mod_perl2 or require mod_perl };
unless ($@) {
$wanted = $mod_perl::VERSION >= 1.99 ? 2 : 1;
}
}
return $wanted;
}
# the function looks at %ENV and Makefile.PL option to figure out
# whether a specific mod_perl generation was requested.
# It uses the following logic:
# via options:
# perl Makefile.PL MOD_PERL=2
# or via %ENV:
# env MOD_PERL=1 perl Makefile.PL
#
# return value is:
# 1 or 2 if the specification was found (mp 1 and mp 2 respectively)
# 0 otherwise
sub wanted_mp_generation {
# check if we have a command line specification
# flag: 0: unknown, 1: mp1, 2: mp2
my $flag = 0;
my @pass;
while (@ARGV) {
my $key = shift @ARGV;
if ($key =~ /^MOD_PERL=(\d)$/) {
$flag = $1;
}
else {
push @pass, $key;
}
}
@ARGV = @pass;
# check %ENV
my $env = exists $ENV{MOD_PERL} ? $ENV{MOD_PERL} : 0;
# check for contradicting requirements
if ($env && $flag && $flag != $env) {
die <<EOF;
Can\'t decide which mod_perl version should be used, since you have
supplied contradicting requirements:
enviroment variable MOD_PERL=$env
Makefile.PL option MOD_PERL=$flag
EOF
}
my $wanted = 0;
$wanted = 2 if $env == 2 || $flag == 2;
$wanted = 1 if $env == 1 || $flag == 1;
unless ($wanted) {
# if still unknown try to require mod_perl.pm
eval { require mod_perl2 or require mod_perl };
unless ($@) {
$wanted = $mod_perl::VERSION >= 1.99 ? 2 : 1;
}
}
return $wanted;
}

View file

@ -0,0 +1,5 @@
This package contains an Apache-Test test suite used by
Apache-TestItSelf. We use a dedicated test suite, so we can re-create
cases which normally won't fit into the core Apache-Test test suite.
This is the test suite that should be run from Apache-TestTestItSelf
as explained in Apache-TestItSelf/README

View file

@ -0,0 +1,37 @@
use strict;
use FindBin;
# test against the A-T source lib for easier dev
use lib "$FindBin::Bin/../../../lib";
use lib qw(lib ../lib);
use warnings FATAL => 'all';
use Apache::TestRunPerl ();
package MyTest;
use vars qw(@ISA);
@ISA = qw(Apache::TestRunPerl);
sub new_test_config {
my $self = shift;
#$self->{conf_opts}->{authname} = 'gold club';
return $self->SUPER::new_test_config;
}
sub bug_report {
my $self = shift;
print <<EOI;
+-----------------------------------------------------+
| To report problems please refer to the SUPPORT file |
+-----------------------------------------------------+
EOI
}
MyTest->new->run(@ARGV);

View file

@ -0,0 +1,20 @@
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestRequest;
use Apache::TestUtil;
plan tests => 3, have_lwp;
my $response = GET '/TestBasic__Hello';
ok t_cmp $response->code, 200, '/handler returned HTTP_OK';
ok t_cmp $response->header('Content-Type'), 'text/plain',
'/handler set proper Content-Type';
chomp(my $content = $response->content);
ok t_cmp $content, 'Hello', '/handler returned proper content';

View file

@ -0,0 +1,7 @@
use Apache::TestUtil;
use Apache::TestRequest 'GET_BODY_ASSERT';
my $module = 'TestBasic::Vhost';
my $url = Apache::TestRequest::module2url($module);
t_debug("connecting to $url");
print GET_BODY_ASSERT $url;

View file

@ -0,0 +1,16 @@
# this vhost entry is needed to check that when t/TEST -maxclients 1
# or similar is called after t/TEST -conf was run, and extra.conf
# includes a vhost entry and httpd.conf includes an autogenerated
# vhost entry from some .pm file, we used to have a collision, since
# extra.conf wasn't reparsed and the same port was getting assigned to
# more than one vhost entry, preventing server startup:
#
#default_ VirtualHost overlap on port 8530, the first has precedence
#(98)Address already in use: make_sock: could not bind to address
#0.0.0.0:8530 no listening sockets available, shutting down
#
# XXX: for now using a dummy vhost entry. later if needed to put a
# real vhost entry in ths file, the dummy one can be removed
#
<VirtualHost foo_bar_tar>
</VirtualHost>

View file

@ -0,0 +1,19 @@
package TestBasic::Hello;
use Apache2::RequestRec ();
use Apache2::RequestIO ();
use Apache2::Const -compile => qw(OK);
# XXX: adjust the test that it'll work under mp1 as well
sub handler {
my $r = shift;
$r->content_type('text/plain');
$r->print('Hello');
return Apache2::OK;
}
1;

View file

@ -0,0 +1,28 @@
package TestBasic::Vhost;
use Apache2::Const -compile => qw(OK);
use Apache::Test;
# XXX: adjust the test that it'll work under mp1 as well
sub handler {
my $r = shift;
plan $r, tests => 1;
ok 1;
return Apache2::OK;
}
1;
__END__
<NoAutoConfig>
<VirtualHost TestBasic::Vhost>
<Location /TestBasic__Vhost>
SetHandler modperl
PerlResponseHandler TestBasic::Vhost
</Location>
</VirtualHost>
</NoAutoConfig>

View file

@ -0,0 +1,27 @@
=head1 NAME
Changes - Apache::TestItSelf changes logfile
=head1 Changes
=over 4
=item 0.01
Fix Makefile.PL to make sure that MakeMaker won't descend into
Apache-TestMe. NORECURS doesn't work in older MM versions, so use the
DIR attr as a workaround [Stas]
new test: minmaxclients.t: testing a bug with vhosts reproducable by
t/TEST -conf followed by t/TEST -maxclients 1 [Stas]
new test: interactive.t: the interactive config [Stas]
new test: httpd_arg.t: passing -httpd argument to 'perl Makefile.PL'
and to 't/TEST' [Stas]
starting the config test suite [Stas]
=back
=cut

View file

@ -0,0 +1,40 @@
use 5.005;
use lib qw(../lib); # Apache-Test/lib
use Apache::Test5005compat;
use strict;
use warnings;
use ExtUtils::MakeMaker;
use Apache::TestMM ();
Apache::TestMM::generate_script('t/TEST');
my @clean_files =
qw(t/TEST
Makefile.old
);
my %prereq = (
# needed to be able to use one perl version to drive the test
# suite, but a different version from the tests themselves
'Test::Harness' => '2.44',
);
WriteMakefile(
NAME => 'Apache::TestItSelf',
PREREQ_PM => \%prereq,
VERSION => "0.01",
NORECURS => 1, # don't descend into Apache-TestMe
DIR => [], # NORECURS is broken in older MM
dist => {
COMPRESS => 'gzip -9f', SUFFIX=>'gz',
},
clean => {
FILES => "@clean_files",
},
);

View file

@ -0,0 +1,73 @@
This test suite tests various Apache-Test setups (i.e. whether the
configuration works correctly), something that can't be tested with
the normal run-time test suite.
1) first of all move into Apache-TestItSelf
% chdir Apache-TestItSelf
2) now choose which test suite to run again, to test whether some
changes in A-T break its config, run 2a. But if the config testing
coverage is not complete, try other test suites and then try to
re-create this problem in 2a.
You will need to adjust config files under sample/ to reflect the
location of your preinstalled httpd and mod_perl files.
a. Apache-Test config test suite
% t/TEST -config sample/testitself_conf_apache_test_core.pl
this runs against the test suite under:
Apache-Test/Apache-TestItSelf/Apache-TestMe/t
it's the same as calling:
% t/TEST -base ~/apache.org/Apache-Test/Apache-TestItSelf/Apache-TestMe \
-config sample/testitself_conf_apache_test_core.pl
b. Apache-Test
assuming that Apache-Test is checked out under
~/apache.org/Apache-Test, the following will run the tests against the
Apache-Test test suite
% t/TEST -base ~/apache.org/Apache-Test \
-config sample/testitself_conf_apache_test_core.pl
c. modperl-2.0
assuming that modperl-2.0 is checked out under
~/apache.org/modperl-2.0, the following will run the tests against the
modperl-2.0 test suite
% t/TEST -base ~/apache.org/modperl-2.0 \
-config sample/testitself_conf_mp2_core.pl t/httpd_arg.t t/interactive.t
d. 3rd party modules ###
assuming that Apache-VMonitor-2.0 is checked out under
~/work/modules/Apache-VMonitor-2.0, the following will run the tests
against the Apache-VMonitor-2.0 test suite. of course any other 3rd
party module should do.
% t/TEST -base ~/work/modules/Apache-VMonitor-2.0 \
-config sample/testitself_conf_mp2_modules.pl
-----------------------------
DEBUGGING:
env IPCRUNDEBUG=data t/TEST t/interactive.t
(for more options see IPC::Run / IPC::Run3 manpages)

View file

@ -0,0 +1,160 @@
package MyTest::Util;
use strict;
use warnings FATAL => 'all';
use Apache::TestConfig;
use Apache::TestTrace;
use Exporter ();
use IPC::Run3 ();
use Cwd;
use vars qw(@ISA @EXPORT @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = ();
@EXPORT_OK = qw(myrun3 go_in go_out work_dir dump_stds check_eval
test_configs);
sub myrun3 {
my $cmd = shift;
my $out = '';
my $err = '';
my $ok = IPC::Run3::run3($cmd, \undef, \$out, \$err);
die "IPC::Run3 failed to run $cmd" unless $ok;
dump_stds($cmd, '', $out, $err) if $?;
return ($out, $err);
}
sub go_in {
my $orig_dir = cwd();
my $dir = $ENV{APACHE_TESTITSELF_BASE_DIR} || '';
debug "chdir $dir";
chdir $dir or die "failed to chdir to $dir: $!";
return $orig_dir;
}
sub go_out {
my $dir = shift;
debug "chdir $dir";
chdir $dir or die "failed to chdir to $dir: $!";
}
# the base dir from which the A-T tests are supposed to be run
# we might not be there
sub work_dir { $ENV{APACHE_TESTITSELF_BASE_DIR} }
sub dump_stds {
my($cmd, $in, $out, $err) = @_;
$cmd = 'unknown' unless length $cmd;
$in = '' unless length $in;
$out = '' unless length $out;
$err = '' unless length $err;
if ($cmd) {
$cmd =~ s/\n$//;
$cmd =~ s/^/# /gm;
print STDERR "\n\n#== CMD ===\n$cmd\n#=============";
}
if ($in) {
$in =~ s/\n$//;
$in =~ s/^/# /gm;
print STDERR "\n### STDIN ###\n$in\n##############\n\n\n";
}
if ($out) {
$out =~ s/\n$//;
$out =~ s/^/# /gm;
print STDERR "\n### STDOUT ###\n$out\n##############\n\n\n";
}
if ($err) {
$err =~ s/\n$//;
$err =~ s/^/# /gm;
print STDERR "\n### STDERR ###\n$err\n##############\n\n\n";
}
}
# if $@ is set dumps the $out and $err streams and dies
# otherwise resets the $out and $err streams if $reset_std is true
sub check_eval {
my($cmd, $out, $err, $reset_std, $msg) = @_;
$msg ||= "unknown";
if ($@) {
dump_stds($cmd, '', $out, $err);
die "$@\nError: $msg\n";
}
# reset the streams in caller
($_[1], $_[2]) = ("", "") if $reset_std;
}
# this function returns an array of configs (hashes) coming from
# -config-file command line option
sub test_configs {
my $config_file = $ENV{APACHE_TESTITSELF_CONFIG_FILE} || '';
# reset
%Apache::TestItSelf::Config = ();
@Apache::TestItSelf::Configs = ();
require $config_file;
unless (@Apache::TestItSelf::Configs) {
error "can't find test configs in '$config_file'";
exit 1;
}
my %global = %Apache::TestItSelf::Config;
# merge the global config with instance configs
my @configs = map { { %global, %$_ } } @Apache::TestItSelf::Configs;
return @configs;
}
1;
__END__
=head1 NAME
MyTest::Util -- helper functions
=head1 Config files format
the -config-file command line option specifies which file contains the
configurations to run with.
META: expand
%Apache::TestItSelf::Config = (
perl_exec => "/home/$ENV{USER}/perl/5.8.5-ithread/bin/perl5.8.5",
mp_gen => '2.0',
httpd_gen => '2.0',
httpd_version => 'Apache/2.0.55',
timeout => 200,
makepl_arg => 'MOD_PERL=2 -libmodperl mod_perl-5.8.5-ithread.so',
);
my $path = "/home/$ENV{USER}/httpd";
@Apache::TestItSelf::Configs = (
{
apxs_exec => "$path/prefork/bin/apxs",
httpd_exec => "$path/prefork/bin/httpd",
httpd_mpm => "prefork",
test_verbose => 0,
},
{
apxs_exec => "$path/worker/bin/apxs",
httpd_exec => "$path/worker/bin/httpd",
httpd_mpm => "worker",
test_verbose => 1,
},
);
1;
=cut

View file

@ -0,0 +1,44 @@
# This is a config file for testing Apache-Test
use strict;
use warnings FATAL => 'all';
my $base = "/home/$ENV{USER}";
my $perl_base = "$base/perl";
my $perl_ver = "5.8.8-ithread";
my $PERL = "$perl_base/$perl_ver/bin/perl$perl_ver";
my $httpd_base = "$base/httpd";
my $httpd_gen = '2.0';
my $httpd_ver = 'Apache/2.2.3';
my @mpms = (qw(prefork worker));
my $mp_gen = 2.0;
my $mod_perl_so = "mod_perl-$perl_ver.so";
%Apache::TestItSelf::Config = (
repos_type => 'apache_test_core',
perl_exec => $PERL,
mp_gen => $mp_gen,
httpd_gen => $httpd_gen,
httpd_version => $httpd_ver,
timeout => 200,
test_verbose => 0,
);
@Apache::TestItSelf::Configs = ();
foreach my $mpm (@mpms) {
push @Apache::TestItSelf::Configs,
{
apxs_exec => "$httpd_base/$mpm/bin/apxs",
httpd_exec => "$httpd_base/$mpm/bin/httpd",
httpd_conf => "$httpd_base/$mpm/conf/httpd.conf",
httpd_mpm => $mpm,
makepl_arg => "MOD_PERL=2 -libmodperl $httpd_base/$mpm/modules/$mod_perl_so",
};
}
1;

View file

@ -0,0 +1,43 @@
# This is a config file for testing modperl 2.0 core
use strict;
use warnings FATAL => 'all';
my $base = "/home/$ENV{USER}";
my $perl_base = "$base/perl";
my $perl_ver = "5.8.8-ithread";
my $PERL = "$perl_base/$perl_ver/bin/perl$perl_ver";
my $httpd_base = "$base/httpd/svn";
my $httpd_gen = '2.0';
my $httpd_ver = 'Apache/2.2.3';
my @mpms = (qw(prefork worker));
my $mp_gen = 2.0;
my $mod_perl_so = "mod_perl-$perl_ver.so";
my $common_makepl_arg = "MP_MAINTAINER=1";
%Apache::TestItSelf::Config = (
repos_type => 'mp2_core',
perl_exec => $PERL,
mp_gen => $mp_gen,
httpd_gen => $httpd_gen,
httpd_version => $httpd_ver,,
timeout => 900, # make test may take a long time
test_verbose => 0,
);
@Apache::TestItSelf::Configs = ();
foreach my $mpm (@mpms) {
push @Apache::TestItSelf::Configs,
{
apxs_exec => "$httpd_base/$mpm/bin/apxs",
httpd_exec => "$httpd_base/$mpm/bin/httpd",
httpd_conf => "$httpd_base/$mpm/conf/httpd.conf",
httpd_mpm => $mpm,
makepl_arg => "MP_APXS=$httpd_base/$mpm/bin/apxs $common_makepl_arg",
};
}
1;

View file

@ -0,0 +1,43 @@
# This is a config file for testing modperl 2.0 Apache:: 3rd party modules
use strict;
use warnings FATAL => 'all';
my $base = "/home/$ENV{USER}";
my $perl_base = "$base/perl";
my $perl_ver = "5.8.8-ithread";
my $PERL = "$perl_base/$perl_ver/bin/perl$perl_ver";
my $httpd_base = "$base/httpd/svn";
my $httpd_gen = '2.0';
my $httpd_ver = 'Apache/2.2.3';
my @mpms = (qw(prefork worker));
my $mp_gen = 2.0;
my $mod_perl_so = "mod_perl-$perl_ver.so";
%Apache::TestItSelf::Config = (
repos_type => 'mp2_cpan_modules',
perl_exec => $PERL,
mp_gen => $mp_gen,
httpd_gen => $httpd_gen,
httpd_version => $httpd_ver,
timeout => 200,
test_verbose => 0,
);
@Apache::TestItSelf::Configs = ();
foreach my $mpm (@mpms) {
push @Apache::TestItSelf::Configs,
{
apxs_exec => "$httpd_base/$mpm/bin/apxs",
httpd_exec => "$httpd_base/$mpm/bin/httpd",
httpd_conf => "$httpd_base/$mpm/conf/httpd.conf",
httpd_mpm => $mpm,
makepl_arg => "MOD_PERL=2 -libmodperl $httpd_base/$mpm/modules/$mod_perl_so",
};
}
1;

View file

@ -0,0 +1,123 @@
use strict;
use lib qw(../../lib ../lib ./lib);
use strict;
use warnings FATAL => 'all';
use Test::Harness;
use FindBin;
use File::Spec::Functions qw(catdir);
use Apache::TestTrace;
use Cwd qw(cwd);
use Getopt::Long qw(GetOptions);
my %usage = (
'base-dir' => 'which dir to run the tests in (default: Apache-TestMe)',
'config-file' => 'which config file to use',
'help' => 'display this message',
'trace=T' => 'change tracing default to: warning, notice, ' .
'info, debug, ...',
'verbose[=1]' => 'verbose output',
);
my @flag_opts = qw(verbose help);
my @string_opts = qw(config-file base-dir trace);
my %opts;
# grab from @ARGV only the options that we expect
GetOptions(\%opts, @flag_opts, (map "$_=s", @string_opts));
# t/TEST -v -base /home/$ENV{USER}/apache.org/Apache-Test \
# -config /home/$ENV{USER}/.apache-test/apache_test_config.pm
#
$Test::Harness::verbose = 1 if $opts{verbose};
opt_help() if $opts{help};
opt_help() unless $opts{'config-file'};
if ($opts{'base-dir'}) {
unless (-d $opts{'base-dir'}) {
error "can't find $opts{'base-dir'}";
opt_help();
}
}
else {
my $dir = catdir $FindBin::Bin, qw(.. Apache-TestMe);
# get rid of relative paths
die "can't find the default dir $dir" unless -d $dir;
my $from = cwd();
chdir $dir or die "can't chdir to $dir: $!";
$dir = cwd();
chdir $from or die "can't chdir to $from: $!";
$opts{'base-dir'} = $dir;
}
unless (-r $opts{'config-file'}) {
error "can't read $opts{'config-file'}";
opt_help();
}
if ($opts{trace}) {
my %levels = map {$_ => 1} @Apache::TestTrace::Levels;
if (exists $levels{ $opts{trace} }) {
$Apache::TestTrace::Level = $opts{trace};
# propogate the override for the server-side.
# -trace overrides any previous APACHE_TEST_TRACE_LEVEL settings
$ENV{APACHE_TEST_TRACE_LEVEL} = $opts{trace};
}
else {
error "unknown trace level: $opts{trace}",
"valid levels are: @Apache::TestTrace::Levels";
opt_help();
}
}
# forward the data to the sub-processes run by Test::Harness
$ENV{APACHE_TESTITSELF_CONFIG_FILE} = $opts{'config-file'};
$ENV{APACHE_TESTITSELF_BASE_DIR} = $opts{'base-dir'};
run_my_tests();
sub run_my_tests {
my $base = "t";
unless (-d $base) {
# try to move into the top-level directory
chdir ".." or die "Can't chdir: $!";
}
my @tests;
if (@ARGV) {
for (@ARGV) {
if (-d $_) {
push @tests, <$_/*.t>;
} else {
$_ .= ".t" unless /\.t$/;
push @tests, $_;
}
}
} else {
chdir $base;
@tests = sort (<*.t>);
chdir "..";
@tests = map { "$base/$_" } @tests;
}
runtests @tests;
}
sub opt_help {
print <<EOM;
usage: TEST [options ...]
where options include:
EOM
for (sort keys %usage){
printf " -%-13s %s\n", $_, $usage{$_};
}
exit;
}

View file

@ -0,0 +1,117 @@
#
# basic testing with -httpd argument passed explicitly (to
# Makefile.PL, to t/TEST, etc.)
#
# XXX: -apxs should be really the same test but passing -apxs instead
# of -httpd, so consider to just run both in this test
use strict;
use warnings FATAL => 'all';
use Test::More;
use MyTest::Util qw(myrun3 go_in go_out test_configs);
use Apache::TestConfig ();
my @configs = test_configs();
my $tests_per_config = 18;
plan tests => $tests_per_config * @configs;
my $orig_dir = go_in();
for my $c (@configs) {
Apache::TestConfig::custom_config_nuke();
$ENV{APACHE_TEST_NO_STICKY_PREFERENCES} = 1;
makefile_pl_plus_httpd_arg($c);
# this one will have custom config, but it shouldn't interrupt
# with the explicit one
# XXX: useless at the moment, since the previously stored custom
# config and the explicit config both point to the same config
$ENV{APACHE_TEST_NO_STICKY_PREFERENCES} = 0;
makefile_pl_plus_httpd_arg($c);
Apache::TestConfig::custom_config_nuke();
t_TEST_plus_httpd_arg($c);
}
go_out($orig_dir);
# 6 tests
# explicit Makefile.PL -httpd argument
sub makefile_pl_plus_httpd_arg {
my $c = shift;
my($cmd, $out, $err);
# clean and ignore the results
$cmd = "make clean";
($out, $err) = myrun3($cmd);
my $makepl_arg = $c->{makepl_arg} || '';
$cmd = "$c->{perl_exec} Makefile.PL $makepl_arg " .
"-httpd $c->{httpd_exec} -httpd_conf $c->{httpd_conf}";
($out, $err) = myrun3($cmd);
unlike $err, qr/\[ error\]/, $cmd;
$cmd = "make";
($out, $err) = myrun3($cmd);
is $err, "", $cmd;
my $test_verbose = $c->{test_verbose} ? "TEST_VERBOSE=1" : "";
$cmd = "make test $test_verbose";
($out, $err) = myrun3($cmd);
like $out, qr/using $c->{httpd_version} \($c->{httpd_mpm} MPM\)/, $cmd;
like $out, qr/All tests successful/, $cmd;
unlike $err, qr/\[ error\]/, $cmd;
# test that httpd is found in t/REPORT (if exists)
SKIP: {
$cmd = "t/REPORT";
skip "$cmd doesn't exist", 1 unless -e $cmd;
($out, $err) = myrun3($cmd);
like $out, qr/Server version: $c->{httpd_version}/, $cmd;
}
}
# explicit t/TEST -httpd argument
sub t_TEST_plus_httpd_arg {
my $c = shift;
my($cmd, $out, $err);
# clean and ignore the results
$cmd = "make clean";
($out, $err) = myrun3($cmd);
my $makepl_arg = $c->{makepl_arg} || '';
$cmd = "$c->{perl_exec} Makefile.PL $makepl_arg";
($out, $err) = myrun3($cmd);
unlike $err, qr/\[ error\]/, $cmd;
$cmd = "make";
($out, $err) = myrun3($cmd);
is $err, "", $cmd;
my $test_verbose = $c->{test_verbose} ? "-v " : "";
$cmd = "t/TEST -httpd $c->{httpd_exec} $test_verbose";
($out, $err) = myrun3($cmd);
like $out,
qr/using $c->{httpd_version} \($c->{httpd_mpm} MPM\)/,
$cmd;
like $out, qr/All tests successful/, $cmd;
unlike $err, qr/\[ error\]/, $cmd;
# test that httpd is found in t/REPORT (if exists)
SKIP: {
$cmd = "t/REPORT";
skip "$cmd doesn't exist", 1 unless -e $cmd;
($out, $err) = myrun3($cmd);
like $out, qr/Server version: $c->{httpd_version}/, $cmd;
}
}
__END__

View file

@ -0,0 +1,158 @@
#
# interactive testing (when A-T) can't figure out the configuration
#
use Test::More;
use strict;
use warnings FATAL => 'all';
use IPC::Run qw(start pump finish timeout);
use Cwd qw(cwd);
use File::Spec::Functions qw(catfile);
use MyTest::Util qw(myrun3 go_in go_out work_dir check_eval
test_configs);
use Apache::TestConfig ();
use Apache::TestTrace;
# in this test we don't want any cached preconfiguration to kick in
# A-T is aware of this env var and won't load neither custom config, nor
# Apache/Build.pm from mod_perl2.
local $ENV{APACHE_TEST_INTERACTIVE_CONFIG_TEST} = 1;
my @configs = test_configs();
if ($configs[0]{repos_type} eq 'mp2_core') {
plan skip_all => "modperl2 doesn't run interactive config";
}
else {
my $tests_per_config = 11;
plan tests => $tests_per_config * @configs + 1;
}
my $orig_dir = go_in();
my $cwd = cwd();
my $expected_work_dir = work_dir();
is $cwd, $expected_work_dir, "working in $expected_work_dir";
debug "cwd: $cwd";
for my $c (@configs) {
# install the sticky custom config
install($c);
# interactive config doesn't work with this var on
$ENV{APACHE_TEST_NO_STICKY_PREFERENCES} = 0;
basic($c);
}
go_out($orig_dir);
# 4 tests
sub install {
my $c = shift;
my($cmd, $out, $err);
$cmd = "make clean";
($out, $err) = myrun3($cmd);
# ignore the results
my $makepl_arg = $c->{makepl_arg} || '';
$cmd = "$c->{perl_exec} Makefile.PL $makepl_arg " .
"-httpd $c->{httpd_exec} -apxs $c->{apxs_exec}";
($out, $err) = myrun3($cmd);
my $makefile = catfile $expected_work_dir, "Makefile";
is -e $makefile, 1, "generated $makefile";
unlike $err, qr/\[ error\]/, "checking for errors";
$cmd = "make";
($out, $err) = myrun3($cmd);
is $err, "", $cmd;
$cmd = "make install";
($out, $err) = myrun3($cmd);
unlike $err, qr/\[ error\]/, $cmd;
}
# 7 tests
sub basic {
my $c = shift;
my($cmd, $out, $err);
# clean and ignore the results
$cmd = "make clean";
($out, $err) = myrun3($cmd);
my $makepl_arg = $c->{makepl_arg} || '';
$cmd = "$c->{perl_exec} Makefile.PL $makepl_arg";
($out, $err) = myrun3($cmd);
unlike $err, qr/\[ error\]/, $cmd;
$cmd = "make";
($out, $err) = myrun3($cmd);
is $err, "", $cmd;
{
my $in;
my $expected = '';
my @cmd = qw(make test);
push @cmd, "TEST_VERBOSE=1" if $c->{test_verbose};
$cmd = join " ", @cmd;
# bypass the -t STDIN checks to still ensure the interactive
# config prompts
$ENV{APACHE_TEST_INTERACTIVE_PROMPT_OK} = 1;
$in = '';
$out = '';
$err = '';
my $h = start \@cmd, \$in, \$out, \$err, timeout($c->{timeout});
# here the expect fails if the interactive config doesn't kick
# in, but for example somehow figures out the needed
# information (httpd/apxs) and runs the test suite normally
$expected = "Please provide a full path to 'httpd' executable";
eval { $h->pump until $out =~ /$expected/gc };
my $reset_std = 1;
check_eval($cmd, $out, $err, $reset_std,
"interactive config wasn't invoked");
$in .= "$c->{httpd_exec}\n" ;
$expected = "Please provide a full path to .*? 'apxs' executable";
eval { $h->pump until $out =~ /$expected/gc };
$reset_std = 1;
check_eval($cmd, $out, $err, $reset_std,
"interactive config had a problem");
$in .= "$c->{apxs_exec}\n" ;
eval { $h->finish };
$reset_std = 0; # needed for later sub-tests
check_eval($cmd, $out, $err, $reset_std,
"failed to finish $cmd");
like $out, qr/using $c->{httpd_version} \($c->{httpd_mpm} MPM\)/,
"$cmd: using $c->{httpd_version} \($c->{httpd_mpm} MPM";
like $out, qr/All tests successful/, "$cmd: All tests successful";
unlike $err, qr/\[ error\]/, "$cmd: no error messages";
}
$cmd = "make install";
($out, $err) = myrun3($cmd);
unlike $err, qr/\[ error\]/, $cmd;
# test that httpd is found in t/REPORT (if exists)
SKIP: {
$cmd = "t/REPORT";
skip "$cmd doesn't exist", 1 unless -e $cmd;
($out, $err) = myrun3($cmd);
like $out, qr/Server version: $c->{httpd_version}/, $cmd;
}
}
__END__

View file

@ -0,0 +1,101 @@
#
# -minclients / -maxclients argument passed explicitly (to
# Makefile.PL, to t/TEST, etc.)
#
use strict;
use warnings FATAL => 'all';
use Test::More;
use MyTest::Util qw(myrun3 go_in go_out test_configs);
use Apache::TestConfig ();
my @configs = test_configs();
my $tests_per_config = 18;
plan tests => $tests_per_config * @configs;
my $orig_dir = go_in();
# min/maxclients of 10 should work for pretty much any test suite, so
# for now hardcoded the number in this test
my $clients = 10;
for my $c (@configs) {
for my $opt_name (qw(minclients maxclients)) {
my $opt = "-$opt_name $clients";
makefile_pl_plus_opt($c, $opt);
t_TEST_plus_opt($c, $opt);
}
}
go_out($orig_dir);
# 4 sub tests
# explicit Makefile.PL -(mix|max)clients
sub makefile_pl_plus_opt {
my $c = shift;
my $opt = shift;
my($cmd, $out, $err);
# clean and ignore the results
$cmd = "make clean";
($out, $err) = myrun3($cmd);
my $makepl_arg = $c->{makepl_arg} || '';
$cmd = "$c->{perl_exec} Makefile.PL $makepl_arg $opt " .
"-httpd $c->{httpd_exec} -httpd_conf $c->{httpd_conf}";
($out, $err) = myrun3($cmd);
unlike $err, qr/\[ error\]/, $cmd;
$cmd = "make";
($out, $err) = myrun3($cmd);
is $err, "", $cmd;
my $test_verbose = $c->{test_verbose} ? "TEST_VERBOSE=1" : "";
$cmd = "make test $test_verbose";
($out, $err) = myrun3($cmd);
like $out, qr/All tests successful/, $cmd;
unlike $err, qr/\[ error\]/, $cmd;
}
# 5 tests
# explicit t/TEST -(mix|max)clients
sub t_TEST_plus_opt {
my $c = shift;
my $opt = shift;
my($cmd, $out, $err);
# clean and ignore the results
$cmd = "make clean";
($out, $err) = myrun3($cmd);
my $makepl_arg = $c->{makepl_arg} || '';
$cmd = "$c->{perl_exec} Makefile.PL $makepl_arg";
($out, $err) = myrun3($cmd);
unlike $err, qr/\[ error\]/, $cmd;
$cmd = "make";
($out, $err) = myrun3($cmd);
is $err, "", $cmd;
# the bug was:
# t/TEST -conf
# t/TEST -maxclients 1
#default_ VirtualHost overlap on port 8530, the first has precedence
#(98)Address already in use: make_sock: could not bind to address
#0.0.0.0:8530 no listening sockets available, shutting down
my $test_verbose = $c->{test_verbose} ? "-v " : "";
$cmd = "t/TEST -httpd $c->{httpd_exec} $test_verbose -conf";
($out, $err) = myrun3($cmd);
unlike $err, qr/\[ error\]/, $cmd;
$cmd = "t/TEST -httpd $c->{httpd_exec} $test_verbose $opt";
($out, $err) = myrun3($cmd);
like $out, qr/All tests successful/, $cmd;
unlike $err, qr/\[ error\]/, $cmd;
}
__END__

View file

@ -0,0 +1,26 @@
Apache-Test was originally developed by:
Doug MacEachern <dougm@cpan.org>
Now maintained by:
Fred Moyer <fred@redhotpenguin.com>
Philip M. Gollucci <pgollucci@p6m7g8.com>
with contributions from the following helpful people
(in alphabetical order):
Alessandro Forghieri <Alessandro.Forghieri@think3.com>
David Wheeler <david@kineticode.com>
Gary Benson <gbenson@redhat.com>
Geoffrey Young <geoff@modperlcookbook.org>
Ken Williams <ken@forum.swarthmore.edu>
Randy Kobes <randy@theoryx5.uwinnipeg.ca>
Rick Myers <rik@sumthin.nu>
Stas Bekman <stas@stason.org>
Steve Hay <steve.hay@uk.radan.com>
Steve Piner <stevep@marketview.co.nz>
Tatsuhiko Miyagawa <miyagawa@edge.co.jp>

1262
debian/perl-framework/Apache-Test/Changes vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
to install this module simply follow the canonical procedure
for installing any perl module
$ tar zxvf Apache-Test-1.XX.tar.gz
$ cd Apache-Test-1.XX
$ perl Makefile.PL
$ make
$ sudo make install
if you want to run the tests contained within the distribution
you can point to a suitable Apache distribution via
$ perl Makefile.PL -httpd /path/to/your/apache/bin/httpd
$ make
$ make test
$ sudo make install
for further directions, see the README.

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.

View file

@ -0,0 +1,72 @@
Changes
CONTRIBUTORS
LICENSE
MANIFEST
META.yml
Makefile.PL
README
INSTALL
SUPPORT
ToDo
lib/Apache/Test.pm
lib/Apache/Test5005compat.pm
lib/Apache/TestBuild.pm
lib/Apache/TestClient.pm
lib/Apache/TestCommon.pm
lib/Apache/TestCommonPost.pm
lib/Apache/TestConfig.pm
lib/Apache/TestConfigC.pm
lib/Apache/TestConfigParse.pm
lib/Apache/TestConfigParrot.pm
lib/Apache/TestConfigPerl.pm
lib/Apache/TestConfigPHP.pm
lib/Apache/TestHandler.pm
lib/Apache/TestHarness.pm
lib/Apache/TestHarnessPHP.pm
lib/Apache/TestMB.pm
lib/Apache/TestMM.pm
lib/Apache/TestPerlDB.pm
lib/Apache/TestReport.pm
lib/Apache/TestReportPerl.pm
lib/Apache/TestRequest.pm
lib/Apache/TestRun.pm
lib/Apache/TestRunParrot.pm
lib/Apache/TestRunPerl.pm
lib/Apache/TestRunPHP.pm
lib/Apache/TestSSLCA.pm
lib/Apache/TestServer.pm
lib/Apache/TestSmoke.pm
lib/Apache/TestSmokePerl.pm
lib/Apache/TestSort.pm
lib/Apache/TestTrace.pm
lib/Apache/TestUtil.pm
lib/Bundle/ApacheTest.pm
t/alltest/01bang.t
t/alltest/all.t
t/alltest2/01bang.t
t/alltest2/all.t
t/bad_coding.t
t/cgi-bin/cookies.pl.PL
t/cgi-bin/next_available_port.pl.PL
t/conf/extra.conf.in
t/conf/modperl_extra.pl.in
t/cookies.t
t/import.t
t/log_watch.t
t/more/01testpm.t
t/more/02testmore.t
t/more/03testpm.t
t/more/04testmore.t
t/more/all.t
t/next_available_port.t
t/ping.t
t/redirect.t
t/request.t
t/response/TestMore/testmorepm.pm
t/response/TestMore/testpm.pm
t/sok.t
t/TEST.PL
META.yml Module meta-data (added by MakeMaker)

View file

@ -0,0 +1,20 @@
name: Apache-Test
version_from: lib/Apache/Test.pm
installdirs: site
requires:
Cwd: 2.06
File::Spec: 0.8
distribution_type: module
license: apache_2_0
no_index:
package:
- HTTP::Request::Common
- warnings
- TestMore::testmorepm
- TestMore::testpm
directory:
- Apache-TestItSelf

View file

@ -0,0 +1,208 @@
use 5.005;
use lib qw(lib);
use Apache::Test5005compat;
use strict;
use warnings;
# was this file invoked directly via perl, or via the top-level
# (mp2) Makefile.PL? if top-level, this env var will be set
use constant TOP_LEVEL => $ENV{MOD_PERL_2_BUILD};
if (!TOP_LEVEL) {
# see if we are building from within mp root, add src lib if we are
eval { require File::Spec };
unless ($@) {
if ( -e File::Spec->catdir('..', 'lib') ) {
# building A-T from mp subdirectory, use the mp lib
unshift @INC, File::Spec->catdir('..', 'lib');
}
}
}
use ExtUtils::MakeMaker;
use Symbol;
use File::Find qw(finddepth);
use Apache::TestMM qw(test clean); #enable 'make test and make clean'
use Apache::TestRun;
use Apache::TestTrace;
use Apache::TestReport;
use Apache::TestConfig ();
use Apache::TestRunPerl;
my $VERSION;
set_version();
Apache::TestMM::filter_args();
my @scripts = qw(t/TEST);
finddepth(sub {
return if $_ eq 'Apache-TestItSelf';
return unless /(.*?\.pl)\.PL$/;
push @scripts, "$File::Find::dir/$1";
}, '.');
for (@scripts) {
Apache::TestMM::generate_script($_);
}
Apache::TestReport->generate_script;
my @clean_files =
qw(.mypacklist
t/TEST
t/REPORT
Makefile.old
);
my %prereq = (
'File::Spec' => '0.8',
'Cwd' => '2.06',
);
# Apache::TestServer uses Win32::Process on Windows.
if ($^O =~ /MSWin32/i) {
$prereq{'Win32::Process'} = '0'
}
# Apache-Test/META.yml is excluded from mp2 distro to make PAUSE
# indexer happy, but then perl Makefile.PL complains about a missing
# META.yml, so autogenerate it if it wasn't in the distro
my $no_meta = TOP_LEVEL ? 1 : 0;
WriteMakefile(
NAME => 'Apache::Test',
VERSION => $VERSION,
PREREQ_PM => \%prereq,
NO_META => $no_meta,
dist => {
COMPRESS => 'gzip -9f', SUFFIX => 'gz',
PREOP => 'find $(DISTVNAME) -type d -print|xargs chmod 0755 && ' .
'find $(DISTVNAME) -type f -print|xargs chmod 0644',
TO_UNIX => 'find $(DISTVNAME) -type f -print|xargs dos2unix'
},
clean => {
FILES => "@clean_files",
},
);
# after CPAN/CPANPLUS had a chance to satisfy the requirements,
# enforce those (for those who run things manually)
check_prereqs();
sub check_prereqs {
my %fail = ();
for (sort keys %prereq) {
unless (chk_version($_, $prereq{$_})) {
$fail{$_} = $prereq{$_};
}
}
if (%fail) {
error "\nThe following Apache-Test dependencies aren't satisfied:",
map { "\t$_: $fail{$_}" } sort keys %fail;
error "Install those from http://search.cpan.org and try again";
exit 0;
}
}
sub chk_version {
my($pkg, $wanted) = @_;
no strict 'refs';
local $| = 1;
print "Checking for $pkg...";
(my $p = $pkg . ".pm") =~ s#::#/#g;
eval { require $p;};
print("not ok\n$@"), return if $@;
my $vstr = ${"${pkg}::VERSION"} ? "found v" . ${"${pkg}::VERSION"}
: "not found";
my $vnum = eval(${"${pkg}::VERSION"}) || 0;
print $vnum >= $wanted ? "ok\n" : " " . $vstr . "\n";
$vnum >= $wanted;
}
sub set_version {
$VERSION = $Apache::Test::VERSION;
my $fh = Symbol::gensym();
open $fh, 'Changes' or die "Can't open Changes: $!";
while (<$fh>) {
if(/^=item.*-(dev|rc\d+)/) {
$VERSION .= "-$1";
last;
}
last if /^=item/;
}
close $fh;
}
sub add_dep {
my($string, $targ, $add) = @_;
$$string =~ s/($targ\s+::)/$1 $add/;
}
no warnings 'redefine';
sub MY::postamble {
my $self = shift;
my $q = ($^O =~ /MSWin32/i ? '"' : "'");
my $string = $self->MM::postamble;
$string .= <<"EOF";
tag :
svn copy -m $q\$(VERSION_SYM) tag$q https://svn.apache.org/repos/asf/perl/Apache-Test/trunk https://svn.apache.org/repos/asf/perl/Apache-Test/tags/\$(VERSION_SYM)
EOF
return $string;
}
sub MY::test {
my $self = shift;
# run tests normally if non root user
return $self->Apache::TestMM::test(@_) if (($> != 0) # root user
or (Apache::TestConfig::WINFU)); # win users
# or win32
return <<EOF
test::
\t\@echo
\t\@echo Apache::Test tests cannot be run as the root user.
\t\@echo Apache cannot spawn child processes as 'root', therefore
\t\@echo the test suite must be run with a non privileged user.
\t\@echo Please build Apache::Test as a non-privileged user to
\t\@echo run the test suite.
\t\@echo
EOF
}
sub MY::constants {
my $self = shift;
my $string = $self->MM::constants;
# mp2 installs this into INSTALLSITEARCH, so in order to avoid
# problems when users forget 'make install UNINST=1', trick MM into
# installing pure perl modules to the sitearch location, when this is
# not installed as a part of mp2 build
if (!$ENV{MOD_PERL_2_BUILD}) {
$string .= <<'EOI';
# install into the same location as mod_perl 2.0
INSTALLSITELIB = $(INSTALLSITEARCH)
DESTINSTALLSITELIB = $(DESTINSTALLSITEARCH)
EOI
}
$string;
}

225
debian/perl-framework/Apache-Test/README vendored Normal file
View file

@ -0,0 +1,225 @@
#########
# About #
#########
Apache::Test is a test toolkit for testing an Apache server with any
configuration. It works with Apache 1.3 and Apache 2.0/2.2/2.4 and any
of its modules, including mod_perl 1.0 and 2.0. It was originally developed
for testing mod_perl 2.0.
#################
# Documentation #
#################
For an extensive documentation see the tutorial:
http://perl.apache.org/docs/general/testing/testing.html
and the documentation of the specific Apache::Test modules, which can
be read using 'perldoc', for example:
% perldoc Apache::TestUtil
and the 'Testing mod_perl 2.0' article:
http://www.perl.com/pub/a/2003/05/22/testing.html
###################
# Got a question? #
###################
Post it to the test-dev <at> httpd.apache.org list. The list is
moderated, so unless you are subscribed to it it may take some time
for your post to make it to the list.
For more information see: http://perl.apache.org/projects/Apache-Test/index.html
List Archives:
# www.apachelabs.org
http://www.apachelabs.org/test-dev/
# marc.theaimsgroup.com
http://marc.theaimsgroup.com/?l=apache-modperl-test-dev
# Mbox file
http://perl.apache.org/mail/test-dev/
##############
# Cheat List #
##############
see Makefile.PL for howto enable 'make test'
see t/TEST as an example test harness
see t/*.t for example tests
if the file t/conf/httpd.conf.in exists, it will be used instead of
the default template (in Apache/TestConfig.pm);
if the file t/conf/extra.conf.in exists, it will be used to generate
t/conf/extra.conf with @variable@ substitutions
if the file t/conf/extra.conf exists, it will be included by
httpd.conf
if the file t/conf/modperl_extra.pl exists, it will be included by
httpd.conf as a mod_perl file (PerlRequire)
##################
# Handy examples #
##################
some examples of what i find handy:
see TEST -help for more options
test verbosely
% t/TEST -verbose
start the server
% t/TEST -start
run just this test (if server is running, will not be re-started)
% t/TEST t/apr/table
run just the apr tests
% t/TEST t/apr
run all tests without start/stop of server (e.g. server was started with -d)
% t/TEST -run
stop the server
% t/TEST -stop
ping the server to see whether it runs
% t/TEST -ping
ping the server and wait until the server starts, report waiting time
% t/TEST -ping=block
reconfigure the server, do not run tests
% t/TEST -configure
run as user nobody:
% t/TEST -User nobody
run on a different port:
% t/TEST -Port 8799
let the program pick the next available port (useful when running a
few test sessions on parallel)
% t/TEST -Port select
run on a different server:
% t/TEST -servername example.com
configure an httpd other than the default (that apxs figures out)
% t/TEST -httpd ~/ap/httpd-2.0/httpd
configure a DSO mod_perl object other than the default (that stored in
Apache::BuildConfig)
% t/TEST -libmodperl ~/ap/httpd-2.0/modules/mod_perl-5.8.0.so
or one that can be found relative to LIBEXECDIR
% t/TEST -libmodperl mod_perl-5.6.1.so
switch to another apxs
% t/TEST -apxs ~/ap/httpd-2.0-prefork/bin/apxs
turn on tracing
% t/TEST -preamble "PerlTrace all"
GET url
% t/TEST -get /server-info
HEAD url
% t/TEST -head /server-info
HEAD (no url defaults to /)
% t/TEST -head
GET url with authentication credentials
% t/TEST -get /server-info -username dougm -password foo
POST url (read content from string)
% t/TEST -post /TestApache::post -content 'name=dougm&company=covalent'
POST url (read content from stdin)
% t/TEST -post /TestApache::post -content - < foo.txt
POST url (generate a body of data 1024 bytes in length)
% t/TEST -post /TestApache::post -content x1024
POST url (only print headers, e.g. useful to just check Content-length)
% t/TEST -post -head /TestApache::post -content x100000
GET url (only print headers, e.g. useful to just check Content-length)
% t/TEST -get -head /foo
start server under gdb
% t/TEST -debug
start server under strace (outputs to t/logs/strace.log)
% t/TEST -d strace
run .t test under the perl debugger
% t/TEST -d perl t/modules/access.t
run .t test under the perl debugger (nonstop mode, output to t/logs/perldb.out)
% t/TEST -d perl=nostop t/modules/access.t
control how much noise Apache::Test should produce. to print all the
debug messages:
% t/TEST -trace=debug
to print only warnings and higher trace levels:
% t/TEST -trace=warning
the available modes are:
emerg alert crit error warning notice info debug
turn on -v and LWP trace (1 is the default) mode in Apache::TestRequest
% t/TEST -d lwp t/modules/access.t
turn on -v and LWP trace mode (level 2) in Apache::TestRequest
% t/TEST -d lwp=2 t/modules/access.t
run all tests through mod_ssl
% t/TEST -ssl
run all tests with HTTP/1.1 (keep alive) requests
% t/TEST -http11 -ssl
run all tests with HTTP/1.1 (keep alive) requests through mod_ssl
% t/TEST -http11
run all tests through mod_proxy
% t/TEST -proxy
##################
# Stress testing #
##################
run all tests 10 times in a random order (the seed is autogenerated
and reported)
% t/SMOKE -times=10 -order=random
run all tests 10 times in a random order using the seed obtained from
the previous random run (e.g. 2352211):
% t/SMOKE -times=10 -order=2352211
repeat all tests 10 times (a, b, c, a, b, c)
% t/SMOKE -times=10 -order=repeat
When certain tests fail when running with -times option, you want to
find out the minimal sequence of tests that lead to the
failure. Apache::TestSmoke helps to ease this task, simply run:
% t/SMOKE
which tries various sequences of tests and at the end reports the
shortest sequences found that lead to the same failure.
for more options do:
% t/SMOKE -help

View file

@ -0,0 +1,97 @@
Instructions for Apache-Test Release Manager
0. Ask the PMC to verify that you have the appropriate CPAN permissions
on test-dev@.
1. 'make dist' - to make sure nothing is missing from the manifest,
etc. Now test this generated package (not svn) with as many
configurations as possible on as many platforms as possible.
a. edit ./Changes:
- change -dev to -rc\d+ starting with -rc1
- note that you *do not* want to change the version in Apache/Test.pm,
this is a significant difference than other Apache::* modules.
this means that development proceeds with non '-dev' or '-rc1' version
tags, so keep that in mind.
b. commit Changes
% svn ci -m "1.44 rc1" Changes
c. nuke any preinstalled Apache-Test libs and run 'make test'
d. test that you can 'make install' and then run 'make test' again
e. test whether we are still 100% OK on systems with no LWP:
% APACHE_TEST_PRETEND_NO_LWP=1 make test
2. once confident that the package is good, commit the release candidate
to https://dist.apache.org/repos/dist/dev/perl and post 24 hour-ish
candidate alert to the various lists. note that you will need to
be subscribed to all of the following lists.
o test-dev/perl.apache.org
o dev/perl.apache.org
o modperl/perl.apache.org
o dev/httpd.apache.org
(or maybe longer to give most people a chance to catch up). no need
to tag this package
Subject: [RELEASE CANDIDATE] Apache-Test-1.44 RC\d+
a. if problems are detected during stage 2, repeat stages 1 and 2.
3. when the package has been reported to be good, prepare a new
package to be released
a. edit ./Changes:
- remove -rc\d+
- add release date
b. rerun:
% perl Makefile.PL
make sure tag looks right
% make -n tag
c. commit Changes
% svn ci -m "1.44 release" Changes
d. tag
% make tag
e. create the final package
% make dist
f. test the final package again at least once
4. Upload the package to CPAN
5. Announce the package
a. post to the following lists:
o test-dev/perl.apache.org
o dev/perl.apache.org
o modperl/perl.apache.org
Subject: [ANNOUNCE] Apache-Test-1.44
include:
- MD5 sig (as it comes from CPAN upload announce).
- the latest Changes
6. Prepare for the next cycle
a. increment version in lib/Apache/Test.pm
b. edit ./Changes:
- start a new item with incremented version + '-dev'
=item 1.45-dev
c. bump up version numbers in this file to make it easier to do the
next release.
% perl -pi.bak -e 's/(\d+)\.(\d+)/join(".", $1, $2+1)/eg' RELEASE
d. commit Changes
% svn ci -m "start 1.44-dev cycle" Changes RELEASE lib/Apache/Test.pm

View file

@ -0,0 +1,57 @@
The Apache-Test project is co-maintained by several developers, who
take turns at making CPAN releases. Therefore you may find several
CPAN directories containing Apache-Test releases. The best way to find
the latest release is to use http://search.cpan.org/.
If you have a question or you want to submit a bug report or make a
contribution, please do not email individual authors, but send an
email to the test-dev <at> httpd.apache.org mailing list. This list is
moderated, so unless you are subscribed to it, your message will have
to be approved first by a moderator. Therefore please allow some time
(up to a few days) for your post to propagate to the list.
If 'make test' fails to start, with an error message:
!!! no test server configured, please specify an httpd or apxs or put
either in your PATH. For example: t/TEST -httpd /path/to/bin/httpd
or similar, please don't submit a bug report, since this is a user
error, not a bug in Apache-Test. Instead, do what the error message
suggests; Apache-Test needs to know where it can find Apache and other
components. If you have apxs installed you can run the test suite via:
% t/TEST -apxs /path/to/bin/apxs
or if you set the APXS environment variable, via make:
% APXS=/path/to/bin/apxs make test
If you don't have 'apxs', tell Apache-Test where your httpd can be
found:
% t/TEST -httpd /path/to/bin/httpd
or via the APACHE environment variable:
% APACHE=/path/to/bin/httpd make test
*** CPAN Testers ***
CPAN Testers using CPANPLUS and running under 'root' are required to
update their perl installation to have IPC::Run 0.78 or higher. Please
do not post failure reports unless you have this prerequisite
satisfied. It has nothing to do with Apache-Test itself, and needed
for CPANPLUS to flush the STDERR and STDOUT streams at the right time,
allowing you to skip the test suite if the conditions aren't suitable
for its execution.
*** Apache C modules bug reports ***
It's now possible to easily create tarballs with self-contained bug
reports for Apache modules in C. Geoff developed and maintains the
skeleton:
http://perl.apache.org/~geoff/bug-reporting-skeleton-apache.tar.gz
So next time you want to send a reproducable bug report for some C module,
grab that tarball, put your code in and submit it to the relevant list.

111
debian/perl-framework/Apache-Test/ToDo vendored Normal file
View file

@ -0,0 +1,111 @@
- on linux most symbols are resolved on demand, but this is not the
case with certain other platforms. so testing on linux may not
detect some problems, exposed on other platforms. env var
PERL_DL_NONLAZY=1 tries to resolve all symbols at load time. we
could always enforce that with this patch:
--- Apache-Test/lib/Apache/TestRun.pm 16 Apr 2004 20:29:23 -0000 1.166
+++ Apache-Test/lib/Apache/TestRun.pm 6 May 2004 04:43:01 -0000
@@ -643,7 +643,7 @@
}
close $sh;
- $original_command = "ulimit -c unlimited; $original_command";
+ $original_command = "ulimit -c unlimited; PERL_DL_NONLAZY=1 $original_comma
nd";
- general config: adjust Apache/TestConfig.pm not to write irrelevant
httpd.conf sections (e.g. <IfModule prefork.c> for win32, and vice
versa, A-T knows exactly what mpm it needs to write the config for).
Thus reducing the clutter.
- winnt case: Apache/TestConfig.pm config for <IfModule mpm_winnt.c>
before Apache-2.0.50 ThreadsPerChild had to be at least as big as
the number of Vhosts. This was fixed in 2.0.50. Since A-T knows the
httpd version, it shouldn't start so many threads for httpd >=
2.0.50, but @MinClients@. Also add BACK_COMPAT_MARKER in the logic
so when no longer support httpd < 2.0.50, this logic could be removed.
- sometimes the server aborts completely after the test suite has run
some of the tests (e.g. win32's server has crashed and no
replacement is available), but the client part continues to run
tests unaware of that problem. what would be nice to be able to
detect that the server is gone and somehow abort the test suite
- Custom sticky config: invalidate invalid bits of the saved config,
e.g. if apxs is saved but can't be found on the filesystem. So if
someone installs Apache in one location, runs A-T which saves that
location, and then nukes Apache and reinstalls it into a different
location we should drop the previously saved config since the path
to apxs and/or httpd is now invalid.
- Apache-Test doesn't run on IPv6 systems, need to change the
autogeneration of httpd.conf to support IPv6. It requires a
replacement of 'Listen 80' with 'Listen servername:80'
Philippe posted patch here:
http://marc.theaimsgroup.com/?l=apache-modperl-dev&m=105514290024419&w=2
For now, 127.0.0.1 will be hardcoded in the Listen directive, but a better
method would use this table:
Apache \ OS | IPV4 | IPV6
--------------------------------------------
--enable-v4-mapped | 80 | 80
--disable-v4-mapped | can't happen | 127.0.0.1:80
To more correctly pick the right Listen flavor.
- Apache-Test assumes that any core file found under t/ was generated
by httpd, (and suggests the gdb invoking command) which is incorrect
in some cases. For example t/TEST -config, which is run by bin/perl,
may dump core as well.
- consider not using the __DIE__ sighandler, but instead wrap the
potentially failing code in the eval trap blocks.
- print STDERR is buffered in test handlers, whereas warn() works
normally. select() helps, but STDERR should be unbuffered in first
place.
- If something goes wrong during the ./t/TEST's phase when all the
configuration files httpd.conf, etc. are generated,
t/conf/apache_test_config.pm now gets written, so t/TEST -clean can work
However if the problem happens during 'make test' for
some reason Makefile doesn't abort on exit from test_clean target, no
matter if I put exit -1, 0 or 1, and proceeds with run_tests target.
probably, since __DIE__ will stop the server.
to reproduce the problem during configure() apply this patch:
Index: Apache-Test/lib/Apache/TestConfigPerl.pm
===================================================================
RCS file: /home/cvs/httpd-test/perl-framework/Apache-Test/lib/Apache/TestConfigPerl.pm,v
retrieving revision 1.38
diff -u -r1.38 TestConfigPerl.pm
--- Apache-Test/lib/Apache/TestConfigPerl.pm 2001/10/18 04:18:16 1.38
+++ Apache-Test/lib/Apache/TestConfigPerl.pm 2001/10/19 02:14:56
@@ -347,6 +347,7 @@
if (open $fh, $file) {
my $content = <$fh>;
close $fh;
+ require $file;
if ($content =~ /APACHE_TEST_CONFIGURE/m) {
eval { require $file };
warn $@ if $@;
- segfaults should be trapped and:
* the test routine should be aborted, since it's possible that some
other test will segfault too and overwrite the core file
* it'd be cool to automatically generate the backtrace with help of
Devel::CoreStack and save it in the file
* once we add the backtrace feature, we don't have to abort the rest
of the tests, but to save each bt as "backtrace.$test_path".
=> this should be very useful in smoke testing
* later, it'd be nice to integrate this with build/bugreport.pl, so
the bug report with the backtrace and everything we want to know
from user's machine, including their /etc/shadow (:-) will be
automatically posted to the list.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
# 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::Test5005compat;
use strict;
use Symbol ();
use File::Basename;
use File::Path;
$Apache::Test5005compat::VERSION = '0.01';
my %compat_files = (
'lib/warnings.pm' => \&warnings_pm,
);
sub import {
if ($] >= 5.006) {
#make sure old compat stubs dont wipe out installed versions
unlink for keys %compat_files;
return;
}
eval { require File::Spec::Functions; } or
die "this is only Perl $], you need to install File-Spec from CPAN";
my $min_version = 0.82;
unless ($File::Spec::VERSION >= $min_version) {
die "you need to install File-Spec-$min_version or higher from CPAN";
}
while (my($file, $sub) = each %compat_files) {
$sub->($file);
}
}
sub open_file {
my $file = shift;
unless (-d 'lib') {
$file = "Apache-Test/$file";
}
my $dir = dirname $file;
unless (-d $dir) {
mkpath([$dir], 0, 0755);
}
my $fh = Symbol::gensym();
print "creating $file\n";
open $fh, ">$file" or die "open $file: $!";
return $fh;
}
sub warnings_pm {
return if eval { require warnings };
my $fh = open_file(shift);
print $fh <<'EOF';
package warnings;
sub import {}
1;
EOF
close $fh;
}
1;

View file

@ -0,0 +1,699 @@
# 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::TestBuild;
use strict;
use warnings FATAL => 'all';
use subs qw(system chdir
info warning);
use Config;
use File::Spec::Functions;
use File::Path ();
use Cwd ();
use constant DRYRUN => 0;
my @min_modules = qw(access auth log-config env mime setenvif
mime autoindex dir alias);
my %shared_modules = (
min => join(' ', @min_modules),
);
my %configs = (
all => {
'apache-1.3' => [],
'httpd-2.0' => enable20(qw(modules=all proxy)),
},
most => {
'apache-1.3' => [],
'httpd-2.0' => enable20(qw(modules=most)),
},
min => {
'apache-1.3' => [],
'httpd-2.0' => enable20(@min_modules),
},
exp => {
'apache-1.3' => [],
'httpd-2.0' => enable20(qw(example case_filter
case_filter_in cache
echo deflate bucketeer)),
},
);
my %builds = (
default => {
cflags => '-Wall',
config => {
'apache-1.3' => [],
'httpd-2.0' => [],
},
},
debug => {
cflags => '-g',
config => {
'apache-1.3' => [],
'httpd-2.0' => [qw(--enable-maintainer-mode)],
},
},
prof => {
cflags => '-pg -DGPROF',
},
shared => {
config => {
'apache-1.3' => [],
'httpd-2.0' => enable20_shared('all'),
},
},
mostshared => {
config => {
'apache-1.3' => [],
'httpd-2.0' => enable20_shared('most'),
},
},
minshared => {
config => {
'apache-1.3' => [],
'httpd-2.0' => enable20_shared('min'),
},
},
static => {
},
);
my %mpms = (
default => [qw(prefork worker)],
MSWin32 => [qw(winnt)],
);
my @cvs = qw(httpd-2.0 apache-1.3);
my @dirs = qw(build tar src install);
sub enable20 {
[ map { "--enable-$_" } @_ ];
}
sub enable20_shared {
my $name = shift;
my $modules = $shared_modules{$name} || $name;
enable20(qq(mods-shared="$modules"));
}
sub default_mpms {
$mpms{ $^O } || $mpms{'default'};
}
sub default_dir {
my($self, $dir) = @_;
$self->{$dir} ||= catdir $self->{prefix}, $dir,
}
sub new {
my $class = shift;
#XXX: not generating a BUILD script yet
#this way we can run:
#perl Apache-Test/lib/Apache/TestBuild.pm --cvsroot=anon --foo=...
require Apache::TestConfig;
require Apache::TestTrace;
Apache::TestTrace->import;
my $self = bless {
prefix => '/usr/local/apache',
cwd => Cwd::cwd(),
cvsroot => 'cvs.apache.org:/home/cvs',
cvs => \@cvs,
cvstag => "",
ssldir => "",
mpms => default_mpms(),
make => $Config{make},
builds => {},
name => "",
extra_config => {
'httpd-2.0' => [],
},
@_,
}, $class;
#XXX
if (my $c = $self->{extra_config}->{'2.0'}) {
$self->{extra_config}->{'httpd-2.0'} = $c;
}
for my $dir (@dirs) {
$self->default_dir($dir);
}
if ($self->{ssldir}) {
push @{ $self->{extra_config}->{'httpd-2.0'} },
'--enable-ssl', "--with-ssl=$self->{ssldir}";
}
$self;
}
sub init {
my $self = shift;
for my $dir (@dirs) {
mkpath($self->{$dir});
}
}
use subs qw(symlink unlink);
use File::Basename;
use File::Find;
sub symlink_tree {
my $self = shift;
my $httpd = 'httpd';
my $install = "$self->{install}/bin/$httpd";
my $source = "$self->{build}/.libs/$httpd";
unlink $install;
symlink $source, $install;
my %dir = (apr => 'apr',
aprutil => 'apr-util');
for my $libname (qw(apr aprutil)) {
my $lib = "lib$libname.so.0.0.0";
my $install = "$self->{install}/lib/$lib";
my $source = "$self->{build}/srclib/$dir{$libname}/.libs/$lib";
unlink $install;
symlink $source, $install;
}
$install = "$self->{install}/modules";
$source = "$self->{build}/modules";
for (<$install/*.so>) {
unlink $_;
}
finddepth(sub {
return unless /\.so$/;
my $file = "$File::Find::dir/$_";
symlink $file, "$install/$_";
}, $source);
}
sub unlink {
my $file = shift;
if (-e $file) {
print "unlink $file\n";
}
else {
print "$file does not exist\n";
}
CORE::unlink($file);
}
sub symlink {
my($from, $to) = @_;
print "symlink $from => $to\n";
unless (-e $from) {
print "source $from does not exist\n";
}
my $base = dirname $to;
unless (-e $base) {
print "target dir $base does not exist\n";
}
CORE::symlink($from, $to) or die $!;
}
sub cvs {
my $self = shift;
my $cmd = "cvs -d $self->{cvsroot} @_";
if (DRYRUN) {
info "$cmd";
}
else {
system $cmd;
}
}
my %cvs_names = (
'2.0' => 'httpd-2.0',
'1.3' => 'apache-1.3',
);
my %cvs_snames = (
'2.0' => 'httpd',
'1.3' => 'apache',
);
sub cvs_up {
my($self, $version) = @_;
my $name = $cvs_names{$version};
my $dir = $self->srcdir($version);
if ($self->{cvsroot} eq 'anon') {
$self->{cvsroot} = ':pserver:anoncvs@cvs.apache.org:/home/cvspublic';
unless (-d $dir) {
#XXX do something better than doesn't require prompt if
#we already have an entry in ~/.cvspass
#$self->cvs('login');
warning "may need to run the following command ",
"(password is 'anoncvs')";
warning "cvs -d $self->{cvsroot} login";
}
}
if (-d $dir) {
chdir $dir;
$self->cvs(up => "-dP $self->{cvstag}");
return;
}
my $co = checkout($name);
$self->$co($name, $dir);
my $post = post_checkout($name);
$self->$post($name, $dir);
}
sub checkout_httpd_2_0 {
my($self, $name, $dir) = @_;
my $tag = $self->{cvstag};
$self->cvs(co => "-d $dir $tag $name");
chdir "$dir/srclib";
$self->cvs(co => "$tag apr apr-util");
}
sub checkout_apache_1_3 {
my($self, $name, $dir) = @_;
$self->cvs(co => "-d $dir $self->{cvstag} $name");
}
sub post_checkout_httpd_2_0 {
my($self, $name, $dir) = @_;
}
sub post_checkout_apache_1_3 {
}
sub canon {
my $name = shift;
return $name unless $name;
$name =~ s/[.-]/_/g;
$name;
}
sub checkout {
my $name = canon(shift);
\&{"checkout_$name"};
}
sub post_checkout {
my $name = canon(shift);
\&{"post_checkout_$name"};
}
sub cvs_update {
my $self = shift;
my $cvs = shift || $self->{cvs};
chdir $self->{src};
for my $name (@$cvs) {
$self->cvs_up($name);
}
}
sub merge_build {
my($self, $version, $builds, $configs) = @_;
my $b = {
cflags => $builds{default}->{cflags},
config => [ @{ $builds{default}->{config}->{$version} } ],
};
for my $name (@$builds) {
next if $name eq 'default'; #already have this
if (my $flags = $builds{$name}->{cflags}) {
$b->{cflags} .= " $flags";
}
if (my $cfg = $builds{$name}->{config}) {
if (my $vcfg = $cfg->{$version}) {
push @{ $b->{config} }, @$vcfg;
}
}
}
for my $name (@$configs) {
my $cfg = $configs{$name}->{$version};
next unless $cfg;
push @{ $b->{config} }, @$cfg;
}
if (my $ex = $self->{extra_config}->{$version}) {
push @{ $b->{config} }, @$ex;
}
if (my $ex = $self->{extra_cflags}->{$version}) {
$b->{config} .= " $ex";
}
$b;
}
my @srclib_dirs = qw(
apr apr-util apr-util/xml/expat pcre
);
sub install_name {
my($self, $builds, $configs, $mpm) = @_;
return $self->{name} if $self->{name};
my $name = join '-', $mpm, @$builds, @$configs;
if (my $tag = $self->cvs_name) {
$name .= "-$tag";
}
$name;
}
#currently the httpd-2.0 build does not properly support static linking
#of ssl libs, force the issue
sub add_ssl_libs {
my $self = shift;
my $ssldir = $self->{ssldir};
return unless $ssldir and -d $ssldir;
my $name = $self->{current_install_name};
my $ssl_mod = "$name/modules/ssl";
info "editing $ssl_mod/modules.mk";
if (DRYRUN) {
return;
}
my $ssl_mk = "$self->{build}/$ssl_mod/modules.mk";
open my $fh, $ssl_mk or die "open $ssl_mk: $!";
my @lines = <$fh>;
close $fh;
for (@lines) {
next unless /SH_LINK/;
chomp;
$_ .= " -L$ssldir -lssl -lcrypto\n";
info 'added ssl libs';
last;
}
open $fh, '>', $ssl_mk or die $!;
print $fh join "\n", @lines;
close $fh;
}
sub cvs_name {
my $self = shift;
if (my $tag = $self->{cvstag}) {
$tag =~ s/^-[DAr]//;
$tag =~ s/\"//g;
$tag =~ s,[/ :],_,g; #-D"03/29/02 07:00pm"
return $tag;
}
return "";
}
sub srcdir {
my($self, $src) = @_;
my $prefix = "";
if ($src =~ s/^(apache|httpd)-//) {
$prefix = $1;
}
else {
$prefix = $cvs_snames{$src};
}
if ($src =~ /^\d\.\d$/) {
#release version will be \d\.\d\.\d+
if (my $tag = $self->cvs_name) {
$src .= "-$tag";
}
$src .= '-cvs';
}
join '-', $prefix, $src;
}
sub configure_httpd_2_0 {
my($self, $src, $builds, $configs, $mpm) = @_;
$src = $self->srcdir($src);
chdir $self->{build};
my $name = $self->install_name($builds, $configs, $mpm);
$self->{current_install_name} = $name;
$self->{builds}->{$name} = 1;
if ($self->{fresh}) {
rmtree($name);
}
else {
if (-e "$name/.DONE") {
warning "$name already configured";
warning "rm $name/.DONE to force";
return;
}
}
my $build = $self->merge_build('httpd-2.0', $builds, $configs);
$ENV{CFLAGS} = $build->{cflags};
info "CFLAGS=$ENV{CFLAGS}";
my $prefix = "$self->{install}/$name";
rmtree($prefix) if $self->{fresh};
my $source = "$self->{src}/$src";
my @args = ("--prefix=$prefix",
"--with-mpm=$mpm",
"--srcdir=$source",
@{ $build->{config} });
chdir $source;
system "./buildconf";
my $cmd = "$source/configure @args";
chdir $self->{build};
mkpath($name);
chdir $name;
for my $dir (@srclib_dirs) {
mkpath("srclib/$dir");
}
for my $dir (qw(build docs/conf)) {
mkpath($dir);
}
system $cmd;
open FH, ">.DONE" or die "open .DONE: $!";
print FH scalar localtime;
close FH;
chdir $self->{prefix};
$self->add_ssl_libs;
}
sub make {
my($self, @cmds) = @_;
push @cmds, 'all' unless @cmds;
for my $name (keys %{ $self->{builds} }) {
chdir "$self->{build}/$name";
for my $cmd (@cmds) {
system "$self->{make} $cmd";
}
}
}
sub system {
my $cmd = "@_";
info $cmd;
return if DRYRUN;
unless (CORE::system($cmd) == 0) {
my $status = $? >> 8;
die "system $cmd failed (exit status=$status)";
}
}
sub chdir {
my $dir = shift;
info "chdir $dir";
CORE::chdir $dir;
}
sub mkpath {
my $dir = shift;
return if -d $dir;
info "mkpath $dir";
return if DRYRUN;
File::Path::mkpath([$dir], 1, 0755);
}
sub rmtree {
my $dir = shift;
return unless -d $dir;
info "rmtree $dir";
return if DRYRUN;
File::Path::rmtree([$dir], 1, 1);
}
sub generate_script {
my($class, $file) = @_;
$file ||= catfile 't', 'BUILD';
my $content = join '', <DATA>;
Apache::Test::basic_config()->write_perlscript($file, $content);
}
unless (caller) {
$INC{'Apache/TestBuild.pm'} = __FILE__;
eval join '', <DATA>;
die $@ if $@;
}
1;
__DATA__
use strict;
use warnings FATAL => 'all';
use lib qw(Apache-Test/lib);
use Apache::TestBuild ();
use Getopt::Long qw(GetOptions);
use Cwd ();
my %options = (
prefix => "checkout/build/install prefix",
ssldir => "enable ssl with given directory",
cvstag => "checkout with given cvs tag",
cvsroot => "use 'anon' for anonymous cvs",
version => "apache version (e.g. '2.0')",
mpms => "MPMs to build (e.g. 'prefork')",
flavor => "build flavor (e.g. 'debug shared')",
modules => "enable modules (e.g. 'all exp')",
name => "change name of the build/install directory",
);
my %opts;
Getopt::Long::Configure(qw(pass_through));
#XXX: could be smarter here, being lazy for the moment
GetOptions(\%opts, map "$_=s", sort keys %options);
if (@ARGV) {
print "passing extra args to configure: @ARGV\n";
}
my $home = $ENV{HOME};
$opts{prefix} ||= join '/', Cwd::cwd(), 'farm';
#$opts{ssldir} ||= '';
#$opts{cvstag} ||= '';
#$opts{cvsroot} ||= '';
$opts{version} ||= '2.0';
$opts{mpms} ||= 'prefork';
$opts{flavor} ||= 'debug-shared';
$opts{modules} ||= 'all-exp';
#my @versions = qw(2.0);
#my @mpms = qw(prefork worker perchild);
#my @flavors = ([qw(debug shared)], [qw(prof shared)],
# [qw(debug static)], [qw(prof static)]);
#my @modules = ([qw(all exp)]);
my $split = sub { split '-', delete $opts{ $_[0] } };
my @versions = $opts{version};
my @mpms = $split->('mpms');
my @flavors = ([ $split->('flavor') ]);
my @modules = ([ $split->('modules') ]);
my $tb = Apache::TestBuild->new(fresh => 1,
%opts,
extra_config => {
$opts{version} => \@ARGV,
});
$tb->init;
for my $version (@versions) {
$tb->cvs_update([ $version ]);
for my $mpm (@mpms) {
for my $flavor (@flavors) {
for my $mods (@modules) {
$tb->configure_httpd_2_0($version, $flavor,
$mods, $mpm);
$tb->make(qw(all install));
}
}
}
}

View file

@ -0,0 +1,203 @@
# 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::TestClient;
#this module provides some fallback for when libwww-perl is not installed
#it is by no means an LWP replacement, just enough for very simple requests
#this module does not and will never support certain features such as:
#file upload, http/1.1 (byteranges, keepalive, etc.), following redirects,
#authentication, GET body callbacks, SSL, etc.
use strict;
use warnings FATAL => 'all';
use Apache::TestRequest ();
my $CRLF = "\015\012";
sub request {
my($method, $url, @headers) = @_;
my @real_headers = ();
my $content;
for (my $i = 0; $i < scalar @headers; $i += 2) {
if ($headers[$i] =~ /^content$/i) {
$content = $headers[$i+1];
}
else {
push @real_headers, ($headers[$i], $headers[$i+1]);
}
}
## XXX:
## This is not a FULL URL encode mapping
## space ' '; however is very common, so this
## is useful to convert
$url =~ s/ /%20/g;
my $config = Apache::Test::config();
$method ||= 'GET';
$url ||= '/';
my %headers = ();
my $hostport = Apache::TestRequest::hostport($config);
$headers{Host} = (split ':', $hostport)[0];
my $s = Apache::TestRequest::vhost_socket();
unless ($s) {
warn "cannot connect to $hostport: $!";
return undef;
}
if ($content) {
$headers{'Content-Length'} ||= length $content;
$headers{'Content-Type'} ||= 'application/x-www-form-urlencoded';
}
#for modules/setenvif
$headers{'User-Agent'} ||= 'libwww-perl/0.00';
my $request = join $CRLF,
"$method $url HTTP/1.0",
(map { "$_: $headers{$_}" } keys %headers);
$request .= $CRLF;
for (my $i = 0; $i < scalar @real_headers; $i += 2) {
$request .= "$real_headers[$i]: $real_headers[$i+1]$CRLF";
}
$request .= $CRLF;
# using send() avoids the need to use SIGPIPE if the server aborts
# the connection
$s->send($request);
$s->send($content) if $content;
$request =~ s/\015//g; #for as_string
my $res = {
request => (bless {
headers_as_string => $request,
content => $content || '',
}, 'Apache::TestClientRequest'),
headers_as_string => '',
method => $method,
code => -1, # unknown
};
my($response_line, $header_term);
my $eol = "\015?\012";
local $_;
while (<$s>) {
$res->{headers_as_string} .= $_;
if (m:^(HTTP/\d+\.\d+)[ \t]+(\d+)[ \t]*(.*?)$eol:io) {
$res->{protocol} = $1;
$res->{code} = $2;
$res->{message} = $3;
$response_line = 1;
}
elsif (/^([a-zA-Z0-9_\-]+)\s*:\s*(.*?)$eol/o) {
$res->{headers}->{lc $1} = $2;
}
elsif (/^$eol$/o) {
$header_term = 1;
last;
}
}
unless ($response_line and $header_term) {
warn "malformed response";
}
{
local $/;
$res->{content} = <$s>;
}
close $s;
# an empty body is a valid response
$res->{content} = ''
unless exists $res->{content} and defined $res->{content};
$res->{headers_as_string} =~ s/\015//g; #for as_string
bless $res, 'Apache::TestClientResponse';
}
for my $method (qw(GET HEAD POST PUT)) {
no strict 'refs';
*$method = sub {
my $url = shift;
request($method, $url, @_);
};
}
package Apache::TestClientResponse;
sub header {
my($self, $key) = @_;
$self->{headers}->{lc $key};
}
my @headers = qw(Last-Modified Content-Type);
for my $header (@headers) {
no strict 'refs';
(my $method = lc $header) =~ s/-/_/g;
*$method = sub { shift->{headers}->{lc $header} };
}
sub is_success {
my $code = shift->{code};
return 0 unless defined $code && $code;
$code >= 200 && $code < 300;
}
sub status_line {
my $self = shift;
"$self->{code} $self->{message}";
}
sub as_string {
my $self = shift;
$self->{headers_as_string} . ($self->{content} || '');
}
my @methods = qw(
request protocol code message method
headers_as_string headers content
);
for my $method (@methods) {
no strict 'refs';
*$method = sub {
my($self, $val) = @_;
$self->{$method} = $val if $val;
$self->{$method};
};
}
#inherit headers_as_string, as_string, protocol, content, etc. methods
@Apache::TestClientRequest::ISA = qw(Apache::TestClientResponse);
1;

View file

@ -0,0 +1,109 @@
# 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::TestCommon;
use strict;
use warnings FATAL => 'all';
use File::Basename;
use Apache::Test;
use Apache::TestRequest;
use Apache::TestUtil;
use Apache::TestCommonPost ();
#this module contains common tests that are called from different .t files
#t/apache/passbrigade.t
#t/apache/rwrite.t
sub run_write_test {
my $module = shift;
#1k..9k, 10k..50k, 100k, 300k, 500k, 2Mb, 4Mb, 6Mb, 10Mb
my @sizes = (1..9, 10..50, 100, 300, 500, 2000, 4000, 6000, 10_000);
my @buff_sizes = (1024, 8192);
plan tests => @sizes * @buff_sizes, [$module, 'LWP'];
my $location = "/$module";
my $ua = Apache::TestRequest::user_agent();
for my $buff_size (@buff_sizes) {
for my $size (@sizes) {
my $length = $size * 1024;
my $received = 0;
$ua->do_request(GET => "$location?$buff_size,$length",
sub {
my($chunk, $res) = @_;
$received += length $chunk;
});
ok t_cmp($received, $length, 'bytes in body');
}
}
}
sub run_files_test {
my($verify, $skip_other) = @_;
my $vars = Apache::Test::vars();
my $perlpod = $vars->{perlpod};
my %pod = (
files => [],
num => 0,
url => '/getfiles-perl-pod',
dir => "",
);
if (-d $perlpod) {
my @files = map { basename $_ } <$perlpod/*.pod>;
$pod{files} = \@files;
$pod{num} = scalar @files;
$pod{dir} = $perlpod;
}
else {
push @Apache::Test::SkipReasons,
"dir $vars->{perlpod} does not exist";
}
my %other_files = ();
unless ($skip_other) { #allow to skip the large binary files
%other_files = map {
("/getfiles-binary-$_", $vars->{$_})
} qw(httpd perl);
}
my $tests = $pod{num} + keys(%other_files);
plan tests => $tests, sub { $pod{num} and have_lwp() };
my $ua = Apache::TestRequest::user_agent();
for my $file (@{ $pod{files} }) {
$verify->($ua, "$pod{url}/$file", "$pod{dir}/$file");
}
for my $url (sort keys %other_files) {
$verify->($ua, $url, $other_files{$url});
}
}
1;
__END__

View file

@ -0,0 +1,199 @@
# 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::TestCommonPost;
use strict;
use warnings FATAL => 'all';
use constant POST_HUGE => $ENV{APACHE_TEST_POST_HUGE} || 0;
use Apache::TestRequest ();
use Apache::TestUtil qw(t_cmp t_debug);
use Apache::Test qw(sok);
BEGIN {
my $use_inline = 0;
eval {
#if Inline.pm and libcurl are available
#we can make this test about 3x faster,
#after the inlined code is compiled that is.
require Inline;
Inline->import(C => 'DATA', LIBS => ['-lcurl'],
#CLEAN_AFTER_BUILD => 0,
PREFIX => 'aptest_post_');
*request_init = \&curl_init;
*request_do = \&curl_do;
$use_inline = 1;
} if POST_HUGE;
if (POST_HUGE) {
if ($@) {
t_debug "tests will run faster with Inline and curl installed";
print $@;
}
else {
t_debug "using Inline and curl client";
}
}
unless ($use_inline) {
t_debug "using LWP client";
#fallback to lwp
*request_init = \&lwp_init;
*request_do = \&lwp_do;
}
}
sub lwp_init {
use vars qw($UA $Location);
$UA = Apache::TestRequest::user_agent();
$Location = shift;
}
sub lwp_do {
my $length = shift;
my $request = HTTP::Request->new(POST => $Location);
$request->header('Content-length' => $length);
if (LWP->VERSION >= 5.800) {
$request->content_ref(\('a' x $length));
} else {
# before LWP 5.800 there was no way to tell HTTP::Message not
# to copy the string, there is a settable content_ref since
# 5.800
use constant BUF_SIZE => 8192;
my $remain = $length;
my $content = sub {
my $bytes = $remain < BUF_SIZE ? $remain : BUF_SIZE;
my $buf = 'a' x $bytes;
$remain -= $bytes;
$buf;
};
$request->content($content);
}
my $response = $UA->request($request);
Apache::TestRequest::lwp_trace($response);
return $response->content;
}
my @run_post_test_small_sizes =
#1k..9k, 10k..50k, 100k
(1..9, 10..50, 100);
my @run_post_test_sizes = @run_post_test_small_sizes;
if (POST_HUGE) {
push @run_post_test_sizes,
#300k, 500k, 2Mb, 4Mb, 6Mb, 10Mb
300, 500, 2000, 4000, 6000, 10_000;
}
sub Apache::TestCommon::run_post_test_sizes { @run_post_test_sizes }
sub Apache::TestCommon::run_post_test {
my $module = shift;
my $sizes = shift || \@run_post_test_sizes;
my $location = Apache::TestRequest::resolve_url("/$module");
request_init($location);
for my $size (@$sizes) {
sok {
my $length = ($size * 1024);
my $str = request_do($length);
chomp $str;
t_cmp($length, $str, "length posted");
};
}
}
1;
__DATA__
__C__
#include <curl/curl.h>
#include <curl/easy.h>
static CURL *curl = NULL;
static SV *response = (SV *)NULL;
static long total = 0;
static size_t my_curl_read(char *buffer, size_t size,
size_t nitems, void *data)
{
size_t bytes = nitems < total ? nitems : total;
memset(buffer, 'a', bytes);
total -= bytes;
return bytes;
}
static size_t my_curl_write(char *buffer, size_t size,
size_t nitems, void *data)
{
sv_catpvn(response, buffer, nitems);
return nitems;
}
void aptest_post_curl_init(char *url)
{
char *proto = "HTTP/1.1"; /* curl default */
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_MUTE, 1);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_curl_read);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_curl_write);
if (!getenv("APACHE_TEST_HTTP11")) {
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
proto = "HTTP/1.0";
}
fprintf(stdout, "#CURL using protocol %s\n", proto);
fflush(stdout);
response = newSV(0);
}
SV *aptest_post_curl_do(long len)
{
sv_setpv(response, "");
total = len;
curl_easy_setopt(curl, CURLOPT_INFILESIZE, len);
curl_easy_perform(curl);
return SvREFCNT_inc(response);
}
void aptest_post_END(void)
{
if (response) {
SvREFCNT_dec(response);
}
if (curl) {
curl_easy_cleanup(curl);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,492 @@
# 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::TestConfig; #not TestConfigC on purpose
use strict;
use warnings FATAL => 'all';
use Config;
use Apache::TestConfig ();
use Apache::TestConfigPerl ();
use Apache::TestTrace;
use File::Find qw(finddepth);
sub cmodule_find {
my($self, $mod) = @_;
return unless $mod =~ /^mod_(\w+)\.c$/;
my $sym = $1;
my $dir = $File::Find::dir;
my $file = catfile $dir, $mod;
unless ($self->{APXS}) {
$self->{cmodules_disabled}->{$mod} = "no apxs configured";
return;
}
my $fh = Symbol::gensym();
open $fh, $file or die "open $file: $!";
my $v = <$fh>;
if ($v =~ /^\#define\s+HTTPD_TEST_REQUIRE_APACHE\s+(\d+)\s*$/) {
#define HTTPD_TEST_REQUIRE_APACHE 1
unless ($self->{server}->{rev} == $1) {
my $reason = "requires Apache version $1";
$self->{cmodules_disabled}->{$mod} = $reason;
notice "$mod $reason, skipping.";
return;
}
}
elsif ($v =~ /^\#define\s+HTTPD_TEST_REQUIRE_APACHE\s+(\d\.\d+(\.\d+)?)/) {
#define HTTPD_TEST_REQUIRE_APACHE 2.1
my $wanted = $1;
(my $current) = $self->{server}->{version} =~ m:^Apache/(\d\.\d+\.\d+):;
if (Apache::Test::normalize_vstring($current) <
Apache::Test::normalize_vstring($wanted)) {
my $reason = "requires Apache version $wanted";
$self->{cmodules_disabled}->{$mod} = $reason;
notice "$mod $reason, skipping.";
return;
}
}
close $fh;
push @{ $self->{cmodules} }, {
name => "mod_$sym",
sym => "${sym}_module",
dir => $dir,
subdir => basename $dir,
};
}
sub cmodules_configure {
my($self, $dir) = @_;
$self->{cmodules_disabled} = {}; #for have_module to check
$dir ||= catfile $self->{vars}->{top_dir}, 'c-modules';
unless (-d $dir) {
return;
}
$self->{cmodules_dir} = $dir;
finddepth(sub { cmodule_find($self, $_) }, $dir);
unless ($self->{APXS}) {
warning "cannot build c-modules without apxs";
return;
}
$self->cmodules_generate_include;
$self->cmodules_write_makefiles;
$self->cmodules_compile;
$self->cmodules_httpd_conf;
}
sub cmodules_makefile_vars {
return <<EOF;
MAKE = $Config{make}
EOF
}
my %lib_dir = Apache::TestConfig::WIN32
? (1 => "", 2 => "")
: (1 => "", 2 => ".libs/");
sub cmodules_build_so {
my($self, $name) = @_;
$name = "mod_$name" unless $name =~ /^mod_/;
my $libdir = $self->server->version_of(\%lib_dir);
my $lib = "$libdir$name.so";
}
sub cmodules_write_makefiles {
my $self = shift;
my $modules = $self->{cmodules};
for (@$modules) {
$self->cmodules_write_makefile($_);
}
my $file = catfile $self->{cmodules_dir}, 'Makefile';
my $fh = $self->genfile($file);
print $fh $self->cmodules_makefile_vars;
my @dirs = map { $_->{subdir} } @$modules;
my @targets = qw(clean);
my @libs;
for my $dir (@dirs) {
for my $targ (@targets) {
print $fh "$dir-$targ:\n\tcd $dir && \$(MAKE) $targ\n\n";
}
my $lib = $self->cmodules_build_so($dir);
my $cfile = "$dir/mod_$dir.c";
push @libs, "$dir/$lib";
print $fh "$libs[-1]: $cfile\n\tcd $dir && \$(MAKE) $lib\n\n";
}
for my $targ (@targets) {
print $fh "$targ: ", (map { "$_-$targ " } @dirs), "\n\n";
}
print $fh "all: @libs\n\n";
close $fh or die "close $file: $!";
}
sub cmodules_write_makefile {
my ($self, $mod) = @_;
my $write = \&{"cmodules_write_makefile_$^O"};
$write = \&cmodules_write_makefile_default unless defined &$write;
$write->($self, $mod);
}
sub cmodules_write_makefile_default {
my($self, $mod) = @_;
my $dversion = $self->server->dversion;
my $name = $mod->{name};
my $makefile = catfile $mod->{dir}, 'Makefile';
my $extra = $ENV{EXTRA_CFLAGS} || '';
debug "writing $makefile";
my $lib = $self->cmodules_build_so($name);
my $fh = $self->genfile($makefile);
print $fh <<EOF;
APXS=$self->{APXS}
all: $lib
$lib: $name.c
\$(APXS) $dversion $extra -I$self->{cmodules_dir} -c $name.c
clean:
-rm -rf $name.o $name.lo $name.slo $name.la $name.i $name.s $name.gcno .libs
EOF
close $fh or die "close $makefile: $!";
}
sub cmodules_write_makefile_aix {
my($self, $mod) = @_;
my $dversion = $self->server->dversion;
my $name = $mod->{name};
my $makefile = catfile $mod->{dir}, 'Makefile';
my $apxsflags = '';
#
# Only do this for Apache 1.*
#
if ($self->server->{rev} == 1) {
$apxsflags = "-Wl,-bE:$name.exp";
my $expfile = catfile $mod->{dir}, "$name.exp";
if (! -f $expfile) {
my $fh = Symbol::gensym();
$name =~ /^mod_(\w+)(?:\.c)?$/;
my $sym = $1 . '_module';
open $fh, ">$expfile" or die "open $expfile: $!";
print $fh "$sym\n";
close $fh;
}
}
my $extra = $ENV{EXTRA_CFLAGS} || '';
debug "writing $makefile";
my $lib = $self->cmodules_build_so($name);
my $fh = Symbol::gensym();
open $fh, ">$makefile" or die "open $makefile: $!";
print $fh <<EOF;
APXS=$self->{APXS}
APXSFLAGS=$apxsflags
all: $lib
$lib: $name.c
\$(APXS) $dversion $extra -I$self->{cmodules_dir} \$(APXSFLAGS) -c $name.c
clean:
-rm -rf $name.o $name.lo $name.slo $name.la .libs
EOF
close $fh or die "close $makefile: $!";
}
sub cmodules_write_makefile_MSWin32 {
my($self, $mod) = @_;
my $dversion = $self->server->dversion;
my $name = $mod->{name};
my $makefile = "$mod->{dir}/Makefile";
debug "writing $makefile";
my $extras = '';
my $lib = $self->cmodules_build_so($name);
$extras = ' -llibhttpd -p ' if ($self->server->{rev} != 1);
my $goners = join ' ', (map {$name . '.' . $_} qw(exp lib so lo));
my $fh = Symbol::gensym();
open $fh, ">$makefile" or die "open $makefile: $!";
my $extra = $ENV{EXTRA_CFLAGS} || '';
debug "writing $makefile";
print $fh <<EOF;
APXS=$self->{APXS}
all: $lib
$lib: $name.c
\$(APXS) $dversion $extra -I$self->{cmodules_dir} $extras -c $name.c
clean:
-erase $goners
EOF
close $fh or die "close $makefile: $!";
}
sub cmodules_make {
my $self = shift;
my $targ = shift || 'all';
my $cmd = "cd $self->{cmodules_dir} && $Config{make} $targ";
debug $cmd;
system $cmd;
if ($?) {
die "Failed to build c-modules";
}
}
sub cmodules_compile {
shift->cmodules_make('all');
}
sub cmodules_httpd_conf {
my $self = shift;
my @args;
for my $mod (@{ $self->{cmodules} }) {
my $dir = $mod->{dir};
my $lib = $self->cmodules_build_so($mod->{name});
my $so = "$dir/$lib";
next unless -e $so;
$self->preamble(LoadModule => "$mod->{sym} $so");
my $cname = "$mod->{name}.c";
my $cfile = "$dir/$cname";
$self->{modules}->{$cname} = 1;
$self->add_module_config($cfile, \@args);
}
$self->postamble(\@args) if @args;
}
sub cmodules_clean {
my $self = shift;
my $dir = $self->{cmodules_dir};
return unless $dir and -e "$dir/Makefile";
unless ($self->{clean_level} > 1) {
#skip t/TEST -conf
warning "skipping rebuild of c-modules; run t/TEST -clean to force";
return;
}
$self->cmodules_make('clean');
for my $mod (@{ $self->{cmodules} }) {
my $makefile = "$mod->{dir}/Makefile";
debug "unlink $makefile";
unlink $makefile;
}
unlink "$dir/Makefile";
}
#try making it easier for test modules to compile with both 1.x and 2.x
sub cmodule_define_name {
my $name = shift;
$name eq 'NULL' ? $name : "APACHE_HTTPD_TEST_\U$name";
}
sub cmodule_define {
my $hook = cmodule_define_name(@_);
"#ifndef $hook\n#define $hook NULL\n#endif\n";
}
my @cmodule_config_names = qw(per_dir_create per_dir_merge
per_srv_create per_srv_merge
commands);
my @cmodule_config_defines = map {
cmodule_define($_);
} @cmodule_config_names;
my $cmodule_config_extra =
"#ifndef APACHE_HTTPD_TEST_EXTRA_HOOKS\n".
"#define APACHE_HTTPD_TEST_EXTRA_HOOKS(p) do { } while (0)\n".
"#endif\n";
my $cmodule_config_hooks = join ",\n ", map {
cmodule_define_name($_);
} @cmodule_config_names;
my @cmodule_phases = qw(post_read_request translate_name header_parser
access_checker check_user_id auth_checker
type_checker fixups handler log_transaction
child_init);
my $cmodule_hooks_1 = join ",\n ", map {
cmodule_define_name($_);
} qw(translate_name check_user_id auth_checker access_checker
type_checker fixups log_transaction header_parser
child_init NULL post_read_request);
my $cmodule_template_1 = <<"EOF",
static const handler_rec name ## _handlers[] =
{
{#name, APACHE_HTTPD_TEST_HANDLER}, /* ok if handler is NULL */
{NULL}
};
module MODULE_VAR_EXPORT name ## _module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
$cmodule_config_hooks,
name ## _handlers, /* handlers */
$cmodule_hooks_1
}
EOF
my @cmodule_hooks = map {
my $hook = cmodule_define_name($_);
<<EOF;
if ($hook != NULL)
ap_hook_$_($hook,
NULL, NULL,
APACHE_HTTPD_TEST_HOOK_ORDER);
EOF
} @cmodule_phases;
my @cmodule_hook_defines = map {
cmodule_define($_);
} @cmodule_phases;
my $cmodule_template_2 = <<"EOF";
static void name ## _register_hooks(apr_pool_t *p)
{
@cmodule_hooks
APACHE_HTTPD_TEST_EXTRA_HOOKS(p);
}
module AP_MODULE_DECLARE_DATA name ## _module = {
STANDARD20_MODULE_STUFF,
$cmodule_config_hooks,
name ## _register_hooks, /* register hooks */
}
EOF
my %cmodule_templates = (1 => $cmodule_template_1, 2 => $cmodule_template_2);
sub cmodules_module_template {
my $self = shift;
my $template = $self->server->version_of(\%cmodule_templates);
chomp $template;
$template =~ s,$, \\,mg;
$template =~ s, \\$,,s;
local $" = ', ';
return <<EOF;
#define APACHE_HTTPD_TEST_MODULE(name) \\
$template
EOF
}
sub cmodules_generate_include {
my $self = shift;
my $file = "$self->{cmodules_dir}/apache_httpd_test.h";
my $fh = $self->genfile($file);
while (read Apache::TestConfigC::DATA, my $buf, 1024) {
print $fh $buf;
}
print $fh @cmodule_hook_defines, @cmodule_config_defines;
print $fh $cmodule_config_extra;
print $fh $self->cmodules_module_template;
close $fh;
}
package Apache::TestConfigC; #Apache/TestConfig.pm also has __DATA__
1;
__DATA__
#ifndef APACHE_HTTPD_TEST_H
#define APACHE_HTTPD_TEST_H
/* headers present in both 1.x and 2.x */
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_log.h"
#include "http_main.h"
#include "http_core.h"
#include "ap_config.h"
#ifdef APACHE1
#define AP_METHOD_BIT 1
typedef size_t apr_size_t;
typedef array_header apr_array_header_t;
#define APR_OFF_T_FMT "ld"
#define APR_SIZE_T_FMT "lu"
#endif /* APACHE1 */
#ifdef APACHE2
#ifndef APACHE_HTTPD_TEST_HOOK_ORDER
#define APACHE_HTTPD_TEST_HOOK_ORDER APR_HOOK_MIDDLE
#endif
#include "ap_compat.h"
#endif /* APACHE2 */
#endif /* APACHE_HTTPD_TEST_H */

View file

@ -0,0 +1,781 @@
# 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::TestConfigPHP;
#things specific to php
use strict;
use warnings FATAL => 'all';
use File::Spec::Functions qw(catfile splitdir abs2rel);
use File::Find qw(finddepth);
use Apache::TestTrace;
use Apache::TestRequest;
use Apache::TestConfig;
use Apache::TestConfigPerl;
use Config;
@Apache::TestConfigPHP::ISA = qw(Apache::TestConfig);
my ($php_ini, $test_more);
{
# __DATA__ contains both php.ini and test-more.php
local $/ = "END_OF_FILE\n";
$php_ini = <DATA>;
chomp $php_ini;
$test_more = <DATA>;
chomp $test_more;
}
sub new {
return shift->SUPER::new(@_);
}
my %warn_style = (
html => sub { "<!-- @_ -->" },
c => sub { "/* @_ */" },
ini => sub { join '', grep {s/^/; /gm} @_ },
php => sub { join '', "<?php\n", grep {s/^/# /gm} @_ },
default => sub { join '', grep {s/^/\# /gm} @_ },
);
my %file_ext = (
map({$_ => 'html'} qw(htm html)),
map({$_ => 'c' } qw(c h)),
map({$_ => 'ini' } qw(ini)),
map({$_ => 'php' } qw(php)),
);
sub warn_style_sub_ref {
my ($self, $filename) = @_;
my $ext = $self->filename_ext($filename);
return $warn_style{ $file_ext{$ext} || 'default' };
}
sub configure_php_tests_pick {
my($self, $entries) = @_;
for my $subdir (qw(Response)) {
my $dir = catfile $self->{vars}->{t_dir}, lc $subdir;
next unless -d $dir;
finddepth(sub {
return unless /\.php$/;
my $file = catfile $File::Find::dir, $_;
my $module = abs2rel $file, $dir;
my $status = $self->run_apache_test_config_scan($file);
push @$entries, [$file, $module, $subdir, $status];
}, $dir);
}
}
sub write_php_test {
my($self, $location, $test) = @_;
(my $path = $location) =~ s/test//i;
(my $file = $test) =~ s/php$/t/i;
my $dir = catfile $self->{vars}->{t_dir}, lc $path;
my $t = catfile $dir, $file;
my $php_t = catfile $dir, $test;
return if -e $t;
# don't write out foo.t if foo.php already exists
return if -e $php_t;
$self->gendir($dir);
my $fh = $self->genfile($t);
print $fh <<EOF;
use Apache::TestRequest 'GET_BODY_ASSERT';
print GET_BODY_ASSERT "/$location/$test";
EOF
close $fh or die "close $t: $!";
# write out an all.t file for the directory
# that will skip running all PHP test unless have_php
my $all = catfile $dir, 'all.t';
unless (-e $all) {
my $fh = $self->genfile($all);
print $fh <<EOF;
use strict;
use warnings FATAL => 'all';
use Apache::Test;
# skip all tests in this directory unless a php module is enabled
plan tests => 1, need_php;
ok 1;
EOF
}
}
sub configure_php_inc {
my $self = shift;
my $serverroot = $self->{vars}->{serverroot};
my $path = catfile $serverroot, 'conf';
# make sure that require() or include() calls can find
# the generated test-more.php without using absolute paths
my $cfg = { php_value => "include_path $path", };
$self->postamble(IfModule => $self->{vars}->{php_module}, $cfg);
# give test-more.php access to the ServerRoot directive
$self->postamble("SetEnv SERVER_ROOT $serverroot\n");
}
sub configure_php_functions {
my $self = shift;
my $dir = catfile $self->{vars}->{serverroot}, 'conf';
my $file = catfile $dir, 'test-more.php';
$self->gendir($dir);
my $fh = $self->genfile($file);
print $fh $test_more;
close $fh or die "close $file: $!";
$self->clean_add_file($file);
}
sub configure_php_ini {
my $self = shift;
my $dir = catfile $self->{vars}->{serverroot}, 'conf';
my $file = catfile $dir, 'php.ini';
return if -e $file;
my $log = catfile $self->{vars}->{t_logs}, 'error_log';
$self->gendir($dir);
my $fh = $self->genfile($file);
$php_ini =~ s/\@error_log\@/error_log $log/;
print $fh $php_ini;
close $fh or die "close $file: $!";
$self->clean_add_file($file);
}
sub configure_php_tests {
my $self = shift;
my @entries = ();
$self->configure_php_tests_pick(\@entries);
$self->configure_pm_tests_sort(\@entries);
my %seen = ();
for my $entry (@entries) {
my ($file, $module, $subdir, $status) = @$entry;
my @args = ();
my $directives = $self->add_module_config($file, \@args);
my @parts = splitdir $file;
my $test = pop @parts;
my $location = $parts[-1];
debug "configuring PHP test file $file";
if ($directives->{noautoconfig}) {
$self->postamble(""); # which adds "\n"
}
else {
unless ($seen{$location}++) {
$self->postamble(Alias => [ catfile('', $parts[-1]), catfile(@parts) ]);
my @args = (AddType => 'application/x-httpd-php .php');
$self->postamble(Location => "/$location", \@args);
}
}
$self->write_php_test($location, $test);
}
}
1;
__DATA__
; This is php.ini-recommended from php 5.0.2,
; used in place of your locally installed php.ini file
; as part of the pristine environment Apache-Test creates
; for you
; [NOTE]: cat php.ini-recommended | grep -v '^;' | sed -e '/^$/d'
;
; exceptions to php.ini-recommended are as follows:
display_startup_errors = On
html_errors = Off
@error_log@
output_buffering = Off
; the rest of php.ini-recommended, unaltered, save for
; some tidying like the removal of comments and blank lines
[PHP]
engine = On
zend.ze1_compatibility_mode = Off
short_open_tag = Off
asp_tags = Off
precision = 14
y2k_compliance = On
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func=
serialize_precision = 100
allow_call_time_pass_reference = Off
safe_mode = Off
safe_mode_gid = Off
safe_mode_include_dir =
safe_mode_exec_dir =
safe_mode_allowed_env_vars = PHP_
safe_mode_protected_env_vars = LD_LIBRARY_PATH
disable_functions =
disable_classes =
expose_php = On
max_execution_time = 30 ; Maximum execution time of each script, in seconds
max_input_time = 60 ; Maximum amount of time each script may spend parsing request data
memory_limit = 128M ; Maximum amount of memory a script may consume (128MB)
error_reporting = E_ALL
display_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
variables_order = "GPCS"
register_globals = Off
register_long_arrays = Off
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 8M
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
doc_root =
user_dir =
enable_dl = On
file_uploads = On
upload_max_filesize = 2M
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 60
[Date]
[filter]
[iconv]
[sqlite]
[xmlrpc]
[Pcre]
[Syslog]
define_syslog_variables = Off
[mail function]
SMTP = localhost
smtp_port = 25
[SQL]
sql.safe_mode = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[MySQL]
mysql.allow_persistent = On
mysql.max_persistent = -1
mysql.max_links = -1
mysql.default_port =
mysql.default_socket =
mysql.default_host =
mysql.default_user =
mysql.default_password =
mysql.connect_timeout = 60
mysql.trace_mode = Off
[MySQLi]
mysqli.max_links = -1
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[mSQL]
msql.allow_persistent = On
msql.max_persistent = -1
msql.max_links = -1
[OCI8]
[PostgresSQL]
pgsql.allow_persistent = On
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice = 0
[Sybase]
sybase.allow_persistent = On
sybase.max_persistent = -1
sybase.max_links = -1
sybase.min_error_severity = 10
sybase.min_message_severity = 10
sybase.compatability_mode = Off
[Sybase-CT]
sybct.allow_persistent = On
sybct.max_persistent = -1
sybct.max_links = -1
sybct.min_server_severity = 10
sybct.min_client_severity = 10
[bcmath]
bcmath.scale = 0
[browscap]
[Informix]
ifx.default_host =
ifx.default_user =
ifx.default_password =
ifx.allow_persistent = On
ifx.max_persistent = -1
ifx.max_links = -1
ifx.textasvarchar = 0
ifx.byteasvarchar = 0
ifx.charasvarchar = 0
ifx.blobinfile = 0
ifx.nullformat = 0
[Session]
session.save_handler = files
session.use_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.bug_compat_42 = 0
session.bug_compat_warn = 1
session.referer_check =
session.entropy_length = 0
session.entropy_file =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.hash_function = 0
session.hash_bits_per_character = 5
url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
[MSSQL]
mssql.allow_persistent = On
mssql.max_persistent = -1
mssql.max_links = -1
mssql.min_error_severity = 10
mssql.min_message_severity = 10
mssql.compatability_mode = Off
mssql.secure_connection = Off
[Assertion]
[COM]
[mbstring]
[FrontBase]
[gd]
[exif]
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
END_OF_FILE
/*******************************************************************\
* PROJECT INFORMATION *
* *
* Project: Apache-Test *
* URL: http://perl.apache.org/Apache-Test/ *
* Notice: Copyright (c) 2006 The Apache Software Foundation *
* *
*********************************************************************
* LICENSE INFORMATION *
* *
* Licensed 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. *
* *
*********************************************************************
* MODULE INFORMATION *
* *
* This is a PHP implementation of Test::More: *
* *
* http://search.cpan.org/dist/Test-Simple/lib/Test/More.pm *
* *
*********************************************************************
* CREDITS *
* *
* Originally inspired by work from Andy Lester. Written and *
* maintained by Chris Shiflett. For contact information, see: *
* *
* http://shiflett.org/ *
* *
\*******************************************************************/
header('Content-Type: text/plain');
register_shutdown_function('_test_end');
$_no_plan = FALSE;
$_num_failures = 0;
$_num_skips = 0;
$_test_num = 0;
function plan($plan)
{
/*
plan('no_plan');
plan('skip_all');
plan(array('skip_all' => 'My reason is...'));
plan(23);
*/
global $_no_plan;
global $_skip_all;
global $_skip_reason;
switch ($plan)
{
case 'no_plan':
$_no_plan = TRUE;
break;
case 'skip_all':
echo "1..0\n";
break;
default:
if (is_array($plan))
{
echo "1..0 # Skip {$plan['skip_all']}\n";
exit;
}
echo "1..$plan\n";
break;
}
}
function ok($pass, $test_name = '')
{
global $_test_num;
global $_num_failures;
global $_num_skips;
$_test_num++;
if ($_num_skips)
{
$_num_skips--;
return TRUE;
}
if (!empty($test_name) && $test_name[0] != '#')
{
$test_name = "- $test_name";
}
if ($pass)
{
echo "ok $_test_num $test_name\n";
}
else
{
echo "not ok $_test_num $test_name\n";
$_num_failures++;
$caller = debug_backtrace();
if (strstr($caller['0']['file'], $_SERVER['PHP_SELF']))
{
$file = $caller['0']['file'];
$line = $caller['0']['line'];
}
else
{
$file = $caller['1']['file'];
$line = $caller['1']['line'];
}
$file = str_replace($_SERVER['SERVER_ROOT'], 't', $file);
diag(" Failed test ($file at line $line)");
}
return $pass;
}
function is($this, $that, $test_name = '')
{
$pass = ($this == $that);
ok($pass, $test_name);
if (!$pass)
{
diag(" got: '$this'");
diag(" expected: '$that'");
}
return $pass;
}
function isnt($this, $that, $test_name = '')
{
$pass = ($this != $that);
ok($pass, $test_name);
if (!$pass)
{
diag(" '$this'");
diag(' !=');
diag(" '$that'");
}
return $pass;
}
function like($string, $pattern, $test_name = '')
{
$pass = preg_match($pattern, $string);
ok($pass, $test_name);
if (!$pass)
{
diag(" '$string'");
diag(" doesn't match '$pattern'");
}
return $pass;
}
function unlike($string, $pattern, $test_name = '')
{
$pass = !preg_match($pattern, $string);
ok($pass, $test_name);
if (!$pass)
{
diag(" '$string'");
diag(" matches '$pattern'");
}
return $pass;
}
function cmp_ok($this, $operator, $that, $test_name = '')
{
eval("\$pass = (\$this $operator \$that);");
ok($pass, $test_name);
if (!$pass)
{
diag(" got: '$this'");
diag(" expected: '$that'");
}
return $pass;
}
function can_ok($object, $methods)
{
$pass = TRUE;
$errors = array();
foreach ($methods as $method)
{
if (!method_exists($object, $method))
{
$pass = FALSE;
$errors[] = " method_exists(\$object, $method) failed";
}
}
if ($pass)
{
ok(TRUE, "method_exists(\$object, ...)");
}
else
{
ok(FALSE, "method_exists(\$object, ...)");
diag($errors);
}
return $pass;
}
function isa_ok($object, $expected_class, $object_name = 'The object')
{
$got_class = get_class($object);
if (version_compare(php_version(), '5', '>='))
{
$pass = ($got_class == $expected_class);
}
else
{
$pass = ($got_class == strtolower($expected_class));
}
if ($pass)
{
ok(TRUE, "$object_name isa $expected_class");
}
else
{
ok(FALSE, "$object_name isn't a '$expected_class' it's a '$got_class'");
}
return $pass;
}
function pass($test_name = '')
{
return ok(TRUE, $test_name);
}
function fail($test_name = '')
{
return ok(FALSE, $test_name);
}
function diag($message)
{
if (is_array($message))
{
foreach($message as $current)
{
echo "# $current\n";
}
}
else
{
echo "# $message\n";
}
}
function include_ok($module)
{
$pass = ((include $module) == 'OK');
return ok($pass);
}
function require_ok($module)
{
$pass = ((require $module) == 'OK');
return ok($pass);
}
function skip($message, $num)
{
global $_num_skips;
if ($num < 0)
{
$num = 0;
}
for ($i = 0; $i < $num; $i++)
{
pass("# SKIP $message");
}
$_num_skips = $num;
}
/*
TODO:
function todo()
{
}
function todo_skip()
{
}
function is_deeply()
{
}
function eq_array()
{
}
function eq_hash()
{
}
function eq_set()
{
}
*/
function _test_end()
{
global $_no_plan;
global $_num_failures;
global $_test_num;
if ($_no_plan)
{
echo "1..$_test_num\n";
}
if ($_num_failures)
{
diag("Looks like you failed $_num_failures tests of $_test_num.");
}
}
?>

View file

@ -0,0 +1,92 @@
# 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::TestConfigParrot;
#things specific to mod_parrot
use strict;
use warnings FATAL => 'all';
use File::Spec::Functions qw(catfile splitdir abs2rel);
use File::Find qw(finddepth);
use Apache::TestTrace;
use Apache::TestRequest;
use Apache::TestConfig;
use Apache::TestConfigPerl;
use Config;
@Apache::TestConfigParrot::ISA = qw(Apache::TestConfig);
sub new {
return shift->SUPER::new(@_);
}
sub configure_parrot_tests_pick {
my($self, $entries) = @_;
for my $subdir (qw(Response)) {
my $dir = catfile $self->{vars}->{t_dir}, lc $subdir;
next unless -d $dir;
finddepth(sub {
return unless /\.pir$/;
my $file = catfile $File::Find::dir, $_;
my $module = abs2rel $file, $dir;
my $status = $self->run_apache_test_config_scan($file);
push @$entries, [$file, $module, $subdir, $status];
}, $dir);
}
}
sub configure_parrot_tests {
my $self = shift;
my @entries = ();
$self->configure_parrot_tests_pick(\@entries);
$self->configure_pm_tests_sort(\@entries);
my %seen = ();
for my $entry (@entries) {
my ($file, $module, $subdir, $status) = @$entry;
my @args = ();
my $directives = $self->add_module_config($file, \@args);
$module =~ s,\.pir$,,;
$module =~ s/^[a-z]://i; #strip drive if any
$module = join '::', splitdir $module;
my @base = map { s/^test//i; $_ } split '::', $module;
my $sub = pop @base;
debug "configuring mod_parrot test file $file";
push @args, SetHandler => 'parrot-code';
push @args, ParrotHandler => $module;
$self->postamble(ParrotLoad => $file);
$self->postamble($self->location_container($module), \@args);
$self->write_pm_test($module, lc $sub, map { lc } @base);
}
}
1;
__DATA__

View file

@ -0,0 +1,558 @@
# 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::TestConfig; #not TestConfigParse on purpose
#dont really want/need a full-blown parser
#but do want something somewhat generic
use strict;
use warnings FATAL => 'all';
use Apache::TestTrace;
use File::Spec::Functions qw(rel2abs splitdir file_name_is_absolute);
use File::Basename qw(dirname basename);
sub strip_quotes {
local $_ = shift || $_;
s/^\"//; s/\"$//; $_;
}
my %wanted_config = (
TAKE1 => {map { $_, 1 } qw(ServerRoot ServerAdmin TypesConfig DocumentRoot)},
TAKE2 => {map { $_, 1 } qw(LoadModule LoadFile)},
);
my %spec_init = (
TAKE1 => sub { shift->{+shift} = "" },
TAKE2 => sub { shift->{+shift} = [] },
);
my %spec_apply = (
TypesConfig => \&inherit_server_file,
ServerRoot => sub {}, #dont override $self->{vars}->{serverroot}
DocumentRoot => \&inherit_directive_var,
LoadModule => \&inherit_load_module,
LoadFile => \&inherit_load_file,
);
#where to add config, default is preamble
my %spec_postamble = map { $_, 'postamble' } qw(TypesConfig);
# need to enclose the following directives into <IfModule
# mod_foo.c>..</IfModule>, since mod_foo might be unavailable
my %ifmodule = (
TypesConfig => 'mod_mime.c',
);
sub spec_add_config {
my($self, $directive, $val) = @_;
my $where = $spec_postamble{$directive} || 'preamble';
if (my $ifmodule = $ifmodule{TypesConfig}) {
$self->postamble(<<EOI);
<IfModule $ifmodule>
$directive $val
</IfModule>
EOI
}
else {
$self->$where($directive => $val);
}
}
# resolve relative files like Apache->server_root_relative
# this function doesn't test whether the resolved file exists
sub server_file_rel2abs {
my($self, $file, $base) = @_;
my ($serverroot, $result) = ();
# order search sequence
my @tries = ([ $base,
'user-supplied $base' ],
[ $self->{inherit_config}->{ServerRoot},
'httpd.conf inherited ServerRoot' ],
[ $self->apxs('PREFIX', 1),
'apxs-derived ServerRoot' ]);
# remove surrounding quotes if any
# e.g. Include "/tmp/foo.html"
$file =~ s/^\s*["']?//;
$file =~ s/["']?\s*$//;
if (file_name_is_absolute($file)) {
debug "$file is already absolute";
$result = $file;
}
else {
foreach my $try (@tries) {
next unless defined $try->[0];
if (-d $try->[0]) {
$serverroot = $try->[0];
debug "using $try->[1] to resolve $file";
last;
}
}
if ($serverroot) {
$result = rel2abs $file, $serverroot;
}
else {
warning "unable to resolve $file - cannot find a suitable ServerRoot";
warning "please specify a ServerRoot in your httpd.conf or use apxs";
# return early, skipping file test below
return $file;
}
}
my $dir = dirname $result;
# $file might not exist (e.g. if it's a glob pattern like
# "conf/*.conf" but what we care about here is to check whether
# the base dir was successfully resolved. we don't check whether
# the file exists at all. it's the responsibility of the caller to
# do this check
if (defined $dir && -e $dir && -d _) {
if (-e $result) {
debug "$file successfully resolved to existing file $result";
}
else {
debug "base dir of '$file' successfully resolved to $dir";
}
}
else {
$dir ||= '';
warning "dir '$dir' does not exist (while resolving '$file')";
# old behavior was to return the resolved but non-existent
# file. preserve that behavior and return $result anyway.
}
return $result;
}
sub server_file {
my $f = shift->server_file_rel2abs(@_);
return qq("$f");
}
sub inherit_directive_var {
my($self, $c, $directive) = @_;
$self->{vars}->{"inherit_\L$directive"} = $c->{$directive};
}
sub inherit_server_file {
my($self, $c, $directive) = @_;
$self->spec_add_config($directive,
$self->server_file($c->{$directive}));
}
#so we have the same names if these modules are linked static or shared
my %modname_alias = (
'mod_pop.c' => 'pop_core.c',
'mod_proxy_ajp.c' => 'proxy_ajp.c',
'mod_proxy_http.c' => 'proxy_http.c',
'mod_proxy_ftp.c' => 'proxy_ftp.c',
'mod_proxy_balancer.c' => 'proxy_balancer.c',
'mod_proxy_connect.c' => 'proxy_connect.c',
'mod_modperl.c' => 'mod_perl.c',
);
# Block modules which inhibit testing:
# - mod_jk requires JkWorkerFile or JkWorker to be configured
# skip it for now, tomcat has its own test suite anyhow.
# - mod_casp2 requires other settings in addition to LoadModule
# - mod_bwshare and mod_evasive20 block fast requests that tests are doing
# - mod_fcgid causes https://rt.cpan.org/Public/Bug/Display.html?id=54476
# - mod_modnss.c and mod_rev.c require further configuration
my @autoconfig_skip_module = qw(mod_jk.c mod_casp2.c mod_bwshare.c
mod_fcgid.c mod_evasive20.c mod_modnss.c mod_rev.c);
# add modules to be not inherited from the existing config.
# e.g. prevent from LoadModule perl_module to be included twice, when
# mod_perl already configures LoadModule and it's certainly found in
# the existing httpd.conf installed system-wide.
sub autoconfig_skip_module_add {
push @autoconfig_skip_module, @_;
}
sub should_skip_module {
my($self, $name) = @_;
for (@autoconfig_skip_module) {
if (UNIVERSAL::isa($_, 'Regexp')) {
return 1 if $name =~ /$_/;
}
else {
return 1 if $name eq $_;
}
}
return 0;
}
#inherit LoadModule
sub inherit_load_module {
my($self, $c, $directive) = @_;
for my $args (@{ $c->{$directive} }) {
my $modname = $args->[0];
my $file = $self->server_file_rel2abs($args->[1]);
unless (-e $file) {
debug "$file does not exist, skipping LoadModule";
next;
}
my $name = basename $args->[1];
$name =~ s/\.(s[ol]|dll)$/.c/; #mod_info.so => mod_info.c
$name =~ s/^lib/mod_/; #libphp4.so => mod_php4.c
$name = $modname_alias{$name} if $modname_alias{$name};
# remember all found modules
$self->{modules}->{$name} = $file;
debug "Found: $modname => $name";
if ($self->should_skip_module($name)) {
debug "Skipping LoadModule of $name";
next;
}
debug "LoadModule $modname $name";
# sometimes people have broken system-wide httpd.conf files,
# which include LoadModule of modules, which are built-in, but
# won't be skipped above if they are found in the modules/
# directory. this usually happens when httpd is built once
# with its modules built as shared objects and then again with
# static ones: the old httpd.conf still has the LoadModule
# directives, even though the modules are now built-in
# so we try to workaround this problem using <IfModule>
$self->preamble(IfModule => "!$name",
qq{LoadModule $modname "$file"\n});
}
}
#inherit LoadFile
sub inherit_load_file {
my($self, $c, $directive) = @_;
for my $args (@{ $c->{$directive} }) {
my $file = $self->server_file_rel2abs($args->[0]);
unless (-e $file) {
debug "$file does not exist, skipping LoadFile";
next;
}
if ($self->should_skip_module($args->[0])) {
debug "Skipping LoadFile of $args->[0]";
next;
}
# remember all found modules
push @{$self->{load_file}}, $file;
debug "LoadFile $file";
$self->preamble_first(qq{LoadFile "$file"\n});
}
}
sub parse_take1 {
my($self, $c, $directive) = @_;
$c->{$directive} = strip_quotes;
}
sub parse_take2 {
my($self, $c, $directive) = @_;
push @{ $c->{$directive} }, [map { strip_quotes } split];
}
sub apply_take1 {
my($self, $c, $directive) = @_;
if (exists $self->{vars}->{lc $directive}) {
#override replacement @Variables@
$self->{vars}->{lc $directive} = $c->{$directive};
}
else {
$self->spec_add_config($directive, qq("$c->{$directive}"));
}
}
sub apply_take2 {
my($self, $c, $directive) = @_;
for my $args (@{ $c->{$directive} }) {
$self->spec_add_config($directive => [map { qq("$_") } @$args]);
}
}
sub inherit_config_file_or_directory {
my ($self, $item) = @_;
if (-d $item) {
my $dir = $item;
debug "descending config directory: $dir";
for my $entry (glob "$dir/*") {
$self->inherit_config_file_or_directory($entry);
}
return;
}
my $file = $item;
debug "inheriting config file: $file";
my $fh = Symbol::gensym();
open($fh, $file) or return;
my $c = $self->{inherit_config};
while (<$fh>) {
s/^\s*//; s/\s*$//; s/^\#.*//;
next if /^$/;
# support continuous config lines (which use \ to break the line)
while (s/\\$//) {
my $cont = <$fh>;
$cont =~ s/^\s*//;
$cont =~ s/\s*$//;
$_ .= $cont;
}
(my $directive, $_) = split /\s+/, $_, 2;
if ($directive eq "Include" or $directive eq "IncludeOptional") {
foreach my $include (glob($self->server_file_rel2abs($_))) {
$self->inherit_config_file_or_directory($include);
}
}
#parse what we want
while (my($spec, $wanted) = each %wanted_config) {
next unless $wanted->{$directive};
my $method = "parse_\L$spec";
$self->$method($c, $directive);
}
}
close $fh;
}
sub inherit_config {
my $self = shift;
$self->get_httpd_static_modules;
$self->get_httpd_defines;
#may change after parsing httpd.conf
$self->{vars}->{inherit_documentroot} =
catfile $self->{httpd_basedir}, 'htdocs';
my $file = $self->{vars}->{httpd_conf};
my $extra_file = $self->{vars}->{httpd_conf_extra};
unless ($file and -e $file) {
if (my $base = $self->{httpd_basedir}) {
my $default_conf = $self->{httpd_defines}->{SERVER_CONFIG_FILE};
$default_conf ||= catfile qw(conf httpd.conf);
$file = catfile $base, $default_conf;
# SERVER_CONFIG_FILE might be an absolute path
unless (-e $file) {
if (-e $default_conf) {
$file = $default_conf;
}
else {
# try a little harder
if (my $root = $self->{httpd_defines}->{HTTPD_ROOT}) {
debug "using HTTPD_ROOT to resolve $default_conf";
$file = catfile $root, $default_conf;
}
}
}
}
}
unless ($extra_file and -e $extra_file) {
if ($extra_file and my $base = $self->{httpd_basedir}) {
my $default_conf = catfile qw(conf $extra_file);
$extra_file = catfile $base, $default_conf;
# SERVER_CONFIG_FILE might be an absolute path
$extra_file = $default_conf if !-e $extra_file and -e $default_conf;
}
}
return unless $file or $extra_file;
my $c = $self->{inherit_config};
#initialize array refs and such
while (my($spec, $wanted) = each %wanted_config) {
for my $directive (keys %$wanted) {
$spec_init{$spec}->($c, $directive);
}
}
$self->inherit_config_file_or_directory($file) if $file;
$self->inherit_config_file_or_directory($extra_file) if $extra_file;
#apply what we parsed
while (my($spec, $wanted) = each %wanted_config) {
for my $directive (keys %$wanted) {
next unless $c->{$directive};
my $cv = $spec_apply{$directive} ||
$self->can("apply_\L$directive") ||
$self->can("apply_\L$spec");
$cv->($self, $c, $directive);
}
}
}
sub get_httpd_static_modules {
my $self = shift;
my $httpd = $self->{vars}->{httpd};
return unless $httpd;
$httpd = shell_ready($httpd);
my $cmd = "$httpd -l";
my $list = $self->open_cmd($cmd);
while (<$list>) {
s/\s+$//;
next unless /\.c$/;
chomp;
s/^\s+//;
$self->{modules}->{$_} = 1;
}
close $list;
}
sub get_httpd_defines {
my $self = shift;
my $httpd = $self->{vars}->{httpd};
return unless $httpd;
$httpd = shell_ready($httpd);
my $cmd = "$httpd -V";
my $httpdconf = $self->{vars}->{httpd_conf};
$cmd .= " -f $httpdconf" if $httpdconf;
my $serverroot = $self->{vars}->{serverroot};
$cmd .= " -d $serverroot" if $serverroot;
my $proc = $self->open_cmd($cmd);
while (<$proc>) {
chomp;
if( s/^\s*-D\s*//) {
s/\s+$//;
my($key, $val) = split '=', $_, 2;
$self->{httpd_defines}->{$key} = $val ? strip_quotes($val) : 1;
debug "isolated httpd_defines $key = " . $self->{httpd_defines}->{$key};
}
elsif (/(version|built|module magic number|server mpm):\s+(.*)/i) {
my $val = $2;
(my $key = uc $1) =~ s/\s/_/g;
$self->{httpd_info}->{$key} = $val;
debug "isolated httpd_info $key = " . $val;
}
}
close $proc;
if (my $mmn = $self->{httpd_info}->{MODULE_MAGIC_NUMBER}) {
@{ $self->{httpd_info} }
{qw(MODULE_MAGIC_NUMBER_MAJOR
MODULE_MAGIC_NUMBER_MINOR)} = split ':', $mmn;
}
# get the mpm information where available
# lowercase for consistency across the two extraction methods
# XXX or maybe consider making have_apache_mpm() case-insensitive?
if (my $mpm = $self->{httpd_info}->{SERVER_MPM}) {
# 2.1
$self->{mpm} = lc $mpm;
}
elsif (my $mpm_dir = $self->{httpd_defines}->{APACHE_MPM_DIR}) {
# 2.0
$self->{mpm} = lc basename $mpm_dir;
}
else {
# Apache 1.3 - no mpm to speak of
$self->{mpm} = '';
}
my $version = $self->{httpd_info}->{VERSION} || '';
if ($version =~ qr,Apache/2,) {
# PHP 4.x on httpd-2.x needs a special modname alias:
$modname_alias{'mod_php4.c'} = 'sapi_apache2.c';
}
unless ($version =~ qr,Apache/(2.0|1.3),) {
# for 2.1 and later, mod_proxy_* are really called mod_proxy_*
delete @modname_alias{grep {/^mod_proxy_/} keys %modname_alias};
}
}
sub httpd_version {
my $self = shift;
my $httpd = $self->{vars}->{httpd};
return unless $httpd;
my $version;
$httpd = shell_ready($httpd);
my $cmd = "$httpd -v";
my $v = $self->open_cmd($cmd);
local $_;
while (<$v>) {
next unless s/^Server\s+version:\s*//i;
chomp;
my @parts = split;
foreach (@parts) {
next unless /^Apache\//;
$version = $_;
last;
}
$version ||= $parts[0];
last;
}
close $v;
return $version;
}
sub httpd_mpm {
return shift->{mpm};
}
1;

View file

@ -0,0 +1,654 @@
# 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::TestConfig; #not TestConfigPerl on purpose
#things specific to mod_perl
use strict;
use warnings FATAL => 'all';
use File::Spec::Functions qw(catfile splitdir abs2rel file_name_is_absolute);
use File::Find qw(finddepth);
use Apache::TestTrace;
use Apache::TestRequest;
use Config;
my %libmodperl = (1 => 'libperl.so', 2 => 'mod_perl.so');
sub configure_libmodperl {
my $self = shift;
my $server = $self->{server};
my $libname = $server->version_of(\%libmodperl);
my $vars = $self->{vars};
if ($vars->{libmodperl}) {
# if set, libmodperl was specified from the command line and
# should be used instead of the one that is looked up
# resolve a non-absolute path
$vars->{libmodperl} = $self->find_apache_module($vars->{libmodperl})
unless file_name_is_absolute($vars->{libmodperl});
}
# $server->{rev} could be set to 2 as a fallback, even when
# the wanted version is 1. So check that we use mod_perl 2
elsif ($server->{rev} >= 2 && IS_MOD_PERL_2) {
if (my $build_config = $self->modperl_build_config()) {
if ($build_config->{MODPERL_LIB_SHARED}) {
$libname = $build_config->{MODPERL_LIB_SHARED};
$vars->{libmodperl} ||= $self->find_apache_module($libname);
} else {
$vars->{libmodperl} ||= $self->find_apache_module('mod_perl.so');
}
# XXX: we have a problem with several perl trees pointing
# to the same httpd tree. So it's possible that we
# configure the test suite to run with mod_perl.so built
# against perl which it wasn't built with. Should we use
# something like ldd to check the match?
#
# For now, we'll default to the first mod_perl.so found.
}
else {
# XXX: can we test whether mod_perl was linked statically
# so we don't need to preload it
# if (!linked statically) {
# die "can't find mod_perl built for perl version $]"
# }
error "can't find mod_perl.so built for perl version $]";
}
# don't use find_apache_module or we may end up with the wrong
# shared object, built against different perl
}
else {
# mod_perl 1.0
$vars->{libmodperl} ||= $self->find_apache_module($libname);
# XXX: how do we find out whether we have a static or dynamic
# mod_perl build? die if its dynamic and can't find the module
}
my $cfg = '';
if ($vars->{libmodperl} && -e $vars->{libmodperl}) {
if (Apache::TestConfig::WIN32) {
my $lib = "$Config{installbin}\\$Config{libperl}";
$lib =~ s/lib$/dll/;
$cfg = 'LoadFile ' . qq("$lib"\n) if -e $lib;
}
# add the module we found to the cached modules list
# otherwise have_module('mod_perl') doesn't work unless
# we have a LoadModule in our base config
$self->{modules}->{'mod_perl.c'} = $vars->{libmodperl};
$cfg .= 'LoadModule ' . qq(perl_module "$vars->{libmodperl}"\n);
}
else {
my $msg = "unable to locate $libname (could be a static build)\n";
$cfg = "#$msg";
debug $msg;
}
$self->preamble(IfModule => '!mod_perl.c', $cfg);
}
sub configure_inc {
my $self = shift;
my $top = $self->{vars}->{top_dir};
my $inc = $self->{inc};
for (catdir($top, qw(blib lib)), catdir($top, qw(blib arch))) {
if (-d $_) {
push @$inc, $_;
}
}
# try ../blib as well for Apache::Reload & Co
for (catdir($top, qw(.. blib lib)), catdir($top, qw(.. blib arch))) {
push @$inc, $_ if -d $_;
}
# spec: If PERL5LIB is defined, PERLLIB is not used.
for (qw(PERL5LIB PERLLIB)) {
next unless exists $ENV{$_};
push @$inc, split /$Config{path_sep}/, $ENV{$_};
last;
}
# enable live testing of the Apache-Test dev modules if they are
# located at the project's root dir
my $apache_test_dev_dir = catfile($top, 'Apache-Test', 'lib');
unshift @$inc, $apache_test_dev_dir if -d $apache_test_dev_dir;
}
sub write_pm_test {
my($self, $module, $sub, @base) = @_;
my $dir = catfile $self->{vars}->{t_dir}, @base;
my $t = catfile $dir, "$sub.t";
return if -e $t;
$self->gendir($dir);
my $fh = $self->genfile($t);
my $path = Apache::TestRequest::module2path($module);
print $fh <<EOF;
use Apache::TestRequest 'GET_BODY_ASSERT';
print GET_BODY_ASSERT "/$path";
EOF
close $fh or die "close $t: $!";
}
# propogate PerlPassEnv settings to the server
sub configure_env {
my $self = shift;
$self->preamble(IfModule => 'mod_perl.c',
[ qw(PerlPassEnv APACHE_TEST_TRACE_LEVEL
PerlPassEnv HARNESS_PERL_SWITCHES
PerlPassEnv APACHE_TEST_NO_STICKY_PREFERENCES)
]);
}
sub startup_pl_code {
my $self = shift;
my $serverroot = $self->{vars}->{serverroot};
my $cover = <<'EOF';
if (($ENV{HARNESS_PERL_SWITCHES}||'') =~ m/Devel::Cover/) {
eval {
# 0.48 is the first version of Devel::Cover that can
# really generate mod_perl coverage statistics
require Devel::Cover;
Devel::Cover->VERSION(0.48);
# this ignores coverage data for some generated files
Devel::Cover->import('+inc' => 't/response/',);
1;
} or die "Devel::Cover error: $@";
}
EOF
return <<"EOF";
BEGIN {
use lib '$serverroot';
for my \$file (qw(modperl_inc.pl modperl_extra.pl)) {
eval { require "conf/\$file" } or
die if grep { -e "\$_/conf/\$file" } \@INC;
}
$cover
}
1;
EOF
}
sub configure_startup_pl {
my $self = shift;
#for 2.0 we could just use PerlSwitches -Mlib=...
#but this will work for both 2.0 and 1.xx
if (my $inc = $self->{inc}) {
my $include_pl = catfile $self->{vars}->{t_conf}, 'modperl_inc.pl';
my $fh = $self->genfile($include_pl);
for (reverse @$inc) {
next unless $_;
print $fh "use lib '$_';\n";
}
my $tlib = catdir $self->{vars}->{t_dir}, 'lib';
if (-d $tlib) {
print $fh "use lib '$tlib';\n";
}
# directory for temp packages which can change during testing
# we use require here since a circular dependency exists
# between Apache::TestUtil and Apache::TestConfigPerl, so
# use does not work here
eval { require Apache::TestUtil; };
if ($@) {
die "could not require Apache::TestUtil: $@";
} else {
print $fh "use lib '" . Apache::TestUtil::_temp_package_dir() . "';\n";
}
# if Apache::Test is used to develop a project, we want the
# project/lib directory to be first in @INC (loaded last)
if ($ENV{APACHE_TEST_LIVE_DEV}) {
my $dev_lib = catdir $self->{vars}->{top_dir}, "lib";
print $fh "use lib '$dev_lib';\n" if -d $dev_lib;
}
print $fh "1;\n";
}
if ($self->server->{rev} >= 2) {
$self->postamble(IfModule => 'mod_perl.c',
"PerlSwitches -Mlib=$self->{vars}->{serverroot}\n");
}
my $startup_pl = catfile $self->{vars}->{t_conf}, 'modperl_startup.pl';
unless (-e $startup_pl) {
my $fh = $self->genfile($startup_pl);
print $fh $self->startup_pl_code;
close $fh;
}
$self->postamble(IfModule => 'mod_perl.c',
"PerlRequire $startup_pl\n");
}
my %sethandler_modperl = (1 => 'perl-script', 2 => 'modperl');
sub set_handler {
my($self, $module, $args) = @_;
return if grep { $_ eq 'SetHandler' } @$args;
push @$args,
SetHandler =>
$self->server->version_of(\%sethandler_modperl);
}
sub set_connection_handler {
my($self, $module, $args) = @_;
my $port = $self->new_vhost($module);
my $vars = $self->{vars};
$self->postamble(Listen => '0.0.0.0:' . $port);
}
my %add_hook_config = (
Response => \&set_handler,
ProcessConnection => \&set_connection_handler,
PreConnection => \&set_connection_handler,
);
my %container_config = (
ProcessConnection => \&vhost_container,
PreConnection => \&vhost_container,
);
sub location_container {
my($self, $module) = @_;
my $path = Apache::TestRequest::module2path($module);
Location => "/$path";
}
sub vhost_container {
my($self, $module) = @_;
my $port = $self->{vhosts}->{$module}->{port};
my $namebased = $self->{vhosts}->{$module}->{namebased};
VirtualHost => ($namebased ? '*' : '_default_') . ":$port";
}
sub new_vhost {
my($self, $module, $namebased) = @_;
my($port, $servername, $vhost);
unless ($namebased and exists $self->{vhosts}->{$module}) {
$port = $self->server->select_next_port;
$vhost = $self->{vhosts}->{$module} = {};
$vhost->{port} = $port;
$vhost->{namebased} = $namebased ? 1 : 0;
}
else {
$vhost = $self->{vhosts}->{$module};
$port = $vhost->{port};
# remember the already configured Listen/NameVirtualHost
$vhost->{namebased}++;
}
$servername = $self->{vars}->{servername};
$vhost->{servername} = $servername;
$vhost->{name} = join ':', $servername, $port;
$vhost->{hostport} = $self->hostport($vhost, $module);
$port;
}
my %outside_container = map { $_, 1 } qw{
Alias AliasMatch AddType
PerlChildInitHandler PerlTransHandler PerlPostReadRequestHandler
PerlSwitches PerlRequire PerlModule
};
my %strip_tags = map { $_ => 1} qw(base noautoconfig);
#test .pm's can have configuration after the __DATA__ token
sub add_module_config {
my($self, $module, $args) = @_;
my $fh = Symbol::gensym();
open($fh, $module) or return;
while (<$fh>) {
last if /^(__(DATA|END)__|\#if CONFIG_FOR_HTTPD_TEST)/;
}
my %directives;
while (<$fh>) {
last if /^\#endif/; #for .c modules
next unless /\S+/;
chomp;
s/^\s+//;
$self->replace;
if (/^#/) {
# preserve comments
$self->postamble($_);
next;
}
my($directive, $rest) = split /\s+/, $_, 2;
$directives{$directive}++ unless $directive =~ /^</;
$rest = '' unless defined $rest;
if ($outside_container{$directive}) {
$self->postamble($directive => $rest);
}
elsif ($directive =~ /IfModule/) {
$self->postamble($_);
}
elsif ($directive =~ m/^<(\w+)/) {
# strip special container directives like <Base> and </Base>
my $strip_container = exists $strip_tags{lc $1} ? 1 : 0;
$directives{noautoconfig}++ if lc($1) eq 'noautoconfig';
my $indent = '';
$self->process_container($_, $fh, lc($1),
$strip_container, $indent);
}
else {
push @$args, $directive, $rest;
}
}
\%directives;
}
# recursively process the directives including nested containers,
# re-indent 4 and ucfirst the closing tags letter
sub process_container {
my($self, $first_line, $fh, $directive, $strip_container, $indent) = @_;
my $new_indent = $indent;
unless ($strip_container) {
$new_indent .= " ";
local $_ = $first_line;
s/^\s*//;
$self->replace;
if (/<VirtualHost/) {
$self->process_vhost_open_tag($_, $indent);
}
else {
$self->postamble($indent . $_);
}
}
$self->process_container_remainder($fh, $directive, $new_indent);
unless ($strip_container) {
$self->postamble($indent . "</\u$directive>");
}
}
# processes the body of the container without the last line, including
# the end tag
sub process_container_remainder {
my($self, $fh, $directive, $indent) = @_;
my $end_tag = "</$directive>";
while (<$fh>) {
chomp;
last if m|^\s*\Q$end_tag|i;
s/^\s*//;
$self->replace;
if (m/^\s*<(\w+)/) {
$self->process_container($_, $fh, $1, 0, $indent);
}
else {
$self->postamble($indent . $_);
}
}
}
# does the necessary processing to create a vhost container header
sub process_vhost_open_tag {
my($self, $line, $indent) = @_;
my $cfg = $self->parse_vhost($line);
if ($cfg) {
my $port = $cfg->{port};
$cfg->{out_postamble}->();
$self->postamble($cfg->{line});
$cfg->{in_postamble}->();
} else {
$self->postamble("$indent$line");
}
}
#the idea for each group:
# Response: there will be many of these, mostly modules to test the API
# that plan tests => ... and output with ok()
# the naming allows grouping, making it easier to run an
# individual set of tests, e.g. t/TEST t/apr
# the PerlResponseHandler and SetHandler modperl is auto-configured
# Hooks: for testing the simpler Perl*Handlers
# auto-generates the Perl*Handler config
# Protocol: protocol modules need their own port/vhost to listen on
#@INC is auto-modified so each test .pm can be found
#modules can add their own configuration using __DATA__
my %hooks = map { $_, ucfirst $_ }
qw(init trans headerparser access authen authz type fixup log);
$hooks{Protocol} = 'ProcessConnection';
$hooks{Filter} = 'OutputFilter';
my @extra_subdirs = qw(Response Protocol PreConnection Hooks Filter);
# add the subdirs to @INC early, in case mod_perl is started earlier
sub configure_pm_tests_inc {
my $self = shift;
for my $subdir (@extra_subdirs) {
my $dir = catfile $self->{vars}->{t_dir}, lc $subdir;
next unless -d $dir;
push @{ $self->{inc} }, $dir;
}
}
# @status fields
use constant APACHE_TEST_CONFIGURE => 0;
use constant APACHE_TEST_CONFIG_ORDER => 1;
sub configure_pm_tests_pick {
my($self, $entries) = @_;
for my $subdir (@extra_subdirs) {
my $dir = catfile $self->{vars}->{t_dir}, lc $subdir;
next unless -d $dir;
finddepth(sub {
return unless /\.pm$/;
my $file = catfile $File::Find::dir, $_;
my $module = abs2rel $file, $dir;
my $status = $self->run_apache_test_config_scan($file);
push @$entries, [$file, $module, $subdir, $status];
}, $dir);
}
}
# a simple numerical order is performed and configuration sections are
# inserted using that order. If the test package specifies no special
# token that matches /APACHE_TEST_CONFIG_ORDER\s+([+-]?\d+)/ anywhere
# in the file, 0 is assigned as its order. If the token is specified,
# config section with negative values will be inserted first, with
# positive last. By using different values you can arrange for the
# test configuration sections to be inserted in any desired order
sub configure_pm_tests_sort {
my($self, $entries) = @_;
@$entries = sort {
$a->[3]->[APACHE_TEST_CONFIG_ORDER] <=>
$b->[3]->[APACHE_TEST_CONFIG_ORDER]
} @$entries;
}
sub configure_pm_tests {
my $self = shift;
my @entries = ();
$self->configure_pm_tests_pick(\@entries);
$self->configure_pm_tests_sort(\@entries);
for my $entry (@entries) {
my ($file, $module, $subdir, $status) = @$entry;
my @args = ();
my $file_display;
{
$file_display=$file;
my $topdir=$self->{vars}->{top_dir};
$file_display=~s!^\Q$topdir\E(.)(?:\1)*!!;
}
$self->postamble("\n# included from $file_display");
my $directives = $self->add_module_config($file, \@args);
$module =~ s,\.pm$,,;
$module =~ s/^[a-z]://i; #strip drive if any
$module = join '::', splitdir $module;
$self->run_apache_test_configure($file, $module, $status);
my @base =
map { s/^test//i; $_ } split '::', $module;
my $sub = pop @base;
my $hook = ($subdir eq 'Hooks' ? $hooks{$sub} : '')
|| $hooks{$subdir} || $subdir;
if ($hook eq 'OutputFilter' and $module =~ /::i\w+$/) {
#XXX: tmp hack
$hook = 'InputFilter';
}
my $handler = join $hook, qw(Perl Handler);
if ($self->server->{rev} < 2 and lc($hook) eq 'response') {
$handler =~ s/response//i; #s/PerlResponseHandler/PerlHandler/
}
debug "configuring $module";
unless ($directives->{noautoconfig}) {
if (my $cv = $add_hook_config{$hook}) {
$self->$cv($module, \@args);
}
my $container = $container_config{$hook} || \&location_container;
#unless the .pm test already configured the Perl*Handler
unless ($directives->{$handler}) {
my @handler_cfg = ($handler => $module);
if ($outside_container{$handler}) {
my $cfg = $self->massage_config_args(@handler_cfg);
$self->postamble(IfModule => 'mod_perl.c', $cfg);
} else {
push @args, @handler_cfg;
}
}
if (@args) {
my $cfg = $self->massage_config_args($self->$container($module), \@args);
$self->postamble(IfModule => 'mod_perl.c', $cfg);
}
}
$self->postamble("# end of $file_display\n");
$self->write_pm_test($module, lc $sub, map { lc } @base);
}
}
# scan tests for interesting information
sub run_apache_test_config_scan {
my ($self, $file) = @_;
my @status = ();
$status[APACHE_TEST_CONFIGURE] = 0;
$status[APACHE_TEST_CONFIG_ORDER] = 0;
my $fh = Symbol::gensym();
if (open $fh, $file) {
local $/;
my $content = <$fh>;
close $fh;
# XXX: optimize to match once?
if ($content =~ /APACHE_TEST_CONFIGURE/m) {
$status[APACHE_TEST_CONFIGURE] = 1;
}
if ($content =~ /APACHE_TEST_CONFIG_ORDER\s+([+-]?\d+)/m) {
$status[APACHE_TEST_CONFIG_ORDER] = int $1;
}
}
else {
error "cannot open $file: $!";
}
return \@status;
}
# We have to test whether tests have APACHE_TEST_CONFIGURE() in them
# and run it if found at this stage, so when the server starts
# everything is ready.
# XXX: however we cannot use a simple require() because some tests
# won't require() outside of mod_perl environment. Therefore we scan
# the slurped file in. and if APACHE_TEST_CONFIGURE has been found we
# require the file and run this function.
sub run_apache_test_configure {
my ($self, $file, $module, $status) = @_;
return unless $status->[APACHE_TEST_CONFIGURE];
eval { require $file };
warn $@ if $@;
# double check that it's a real sub
if ($module->can('APACHE_TEST_CONFIGURE')) {
eval { $module->APACHE_TEST_CONFIGURE($self); };
warn $@ if $@;
}
}
1;

View file

@ -0,0 +1,175 @@
# 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::TestHandler;
use strict;
use warnings FATAL => 'all';
use Apache::Test qw/!:DEFAULT/; # call import() to tell about -withouttestmore
use Apache::TestRequest ();
use Apache2::Const -compile => qw(OK NOT_FOUND SERVER_ERROR);
#some utility handlers for testing hooks other than response
#see modperl-2.0/t/hooks/TestHooks/authen.pm
if ($ENV{MOD_PERL} && require mod_perl2) {
require Apache2::RequestRec; # content_type
require Apache2::RequestIO; # puts
}
#compat with 1.xx
my $send_http_header = Apache->can('send_http_header') || sub {};
my $print = Apache2->can('print') || Apache2::RequestRec->can('puts');
sub ok {
my ($r, $boolean) = @_;
$r->$send_http_header;
$r->content_type('text/plain');
$r->$print((@_>1 && !$boolean ? "not " : '')."ok");
0;
}
sub ok1 {
my ($r, $boolean) = @_;
Apache::Test::plan($r, tests => 1);
Apache::Test::ok(@_==1 || $boolean);
0;
}
# a fixup handler to be used when a few requests need to be run
# against the same perl interpreter, in situations where there is more
# than one client running. For an example of use see
# modperl-2.0/t/response/TestModperl/interp.pm and
# modperl-2.0/t/modperl/interp.t
#
# this handler expects the header X-PerlInterpreter in the request
# - if none is set, Apache::SERVER_ERROR is returned
# - if its value eq 'tie', instance's global UUID is assigned and
# returned via the same header
# - otherwise if its value is not the same the stored instance's
# global UUID Apache::NOT_FOUND is returned
#
# in addition $same_interp_counter counts how many times this instance of
# pi has been called after the reset 'tie' request (inclusive), this
# value can be retrieved with Apache::TestHandler::same_interp_counter()
my $same_interp_id = "";
# keep track of how many times this instance was called after the reset
my $same_interp_counter = 0;
sub same_interp_counter { $same_interp_counter }
sub same_interp_fixup {
my $r = shift;
my $interp = $r->headers_in->get(Apache::TestRequest::INTERP_KEY);
unless ($interp) {
# shouldn't be requesting this without an INTERP header
die "can't find the interpreter key";
}
my $id = $same_interp_id;
if ($interp eq 'tie') { #first request for an interpreter instance
# unique id for this instance
$same_interp_id = $id =
unpack "H*", pack "Nnn", time, $$, int(rand(60000));
$same_interp_counter = 0; #reset the counter
}
elsif ($interp ne $same_interp_id) {
# this is not the request interpreter instance
return Apache2::Const::NOT_FOUND;
}
$same_interp_counter++;
# so client can save the created instance id or check the existing
# value
$r->headers_out->set(Apache::TestRequest::INTERP_KEY, $id);
return Apache2::Const::OK;
}
1;
__END__
=encoding utf8
=head1 NAME
Apache::TestHandler - a few response handlers and helpers
=head1 SYNOPSIS
package My::Test;
use Apache::TestHandler ();
sub handler {
my ($r) = @_;
my $result = do_my_test;
Apache::TestHandler::ok1 $r, $result;
}
sub handler2 {
my ($r) = @_;
my $result = do_my_test;
Apache::TestHandler::ok $r, $result;
}
=head1 DESCRIPTION
C<Apache::TestHandler> provides 2 very simple response handler.
=head1 FUNCTIONS
=over 4
=item ok $r, $boolean
The handler simply prints out C<ok> or C<not ok> depending on the
optional C<$boolean> parameter.
If C<$boolean> is omitted C<true> is assumed.
=item ok1 $r, $boolean
This handler implements a simple response-only test. It can be used on its
own to check if for a certain URI the response phase is reached. Or it
can be called like a normal function to print out the test result. The
client side is automatically created as described in
L<http://perl.apache.org/docs/general/testing/testing.html#Developing_Response_only_Part_of_a_Test>.
C<$boolean> is optional. If omitted C<true> is assumed.
=item same_interp_counter
=item same_interp_fixup
TODO
=back
=head1 SEE ALSO
The Apache-Test tutorial:
L<http://perl.apache.org/docs/general/testing/testing.html>.
L<Apache::Test>.
=head1 AUTHOR
Doug MacEachern, Geoffrey Young, Stas Bekman, Torsten Förtsch and others.
Questions can be asked at the test-dev <at> httpd.apache.org list
For more information see: http://httpd.apache.org/test/.
=cut

View file

@ -0,0 +1,199 @@
# 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::TestHarness;
use strict;
use warnings FATAL => 'all';
use Test::Harness ();
use Apache::Test ();
use Apache::TestSort ();
use Apache::TestTrace;
use File::Spec::Functions qw(catfile catdir);
use File::Find qw(finddepth);
use File::Basename qw(dirname);
sub inc_fixup {
# use blib
unshift @INC, map "blib/$_", qw(lib arch);
# fix all relative library locations
for (@INC) {
$_ = "../$_" unless m,^(/)|([a-f]:),i;
}
}
#skip tests listed in t/SKIP
sub skip {
my($self, $file) = @_;
$file ||= catfile Apache::Test::vars('serverroot'), 'SKIP';
return unless -e $file;
my $fh = Symbol::gensym();
open $fh, $file or die "open $file: $!";
my @skip;
local $_;
while (<$fh>) {
chomp;
s/^\s+//; s/\s+$//; s/^\#.*//;
next unless $_;
s/\*/.*/g;
push @skip, $_;
}
close $fh;
return join '|', @skip;
}
#test if all.t would skip tests or not
{
my $source_lib = '';
sub run_t {
my($self, $file) = @_;
my $ran = 0;
if (Apache::TestConfig::IS_APACHE_TEST_BUILD and !length $source_lib) {
# so we can find Apache/Test.pm from both the perl-framework/
# and Apache-Test/
my $top_dir = Apache::Test::vars('top_dir');
foreach my $lib (catfile($top_dir, qw(Apache-Test lib)),
catfile($top_dir, qw(.. Apache-Test lib)),
catfile($top_dir, 'lib')) {
if (-d $lib) {
info "adding source lib $lib to \@INC";
$source_lib = qq[-Mlib="$lib"];
last;
}
}
}
my $cmd = qq[$^X $source_lib $file];
my $h = Symbol::gensym();
open $h, "$cmd|" or die "open $cmd: $!";
local $_;
while (<$h>) {
if (/^1\.\.(\d)/) {
$ran = $1;
last;
}
}
close $h;
$ran;
}
}
#if a directory has an all.t test
#skip all tests in that directory if all.t prints "1..0\n"
sub prune {
my($self, @tests) = @_;
my(@new_tests, %skip_dirs);
foreach my $test (@tests) {
next if $test =~ /\.#/; # skip temp emacs files
my $dir = dirname $test;
if ($test =~ m:\Wall\.t$:) {
unless (__PACKAGE__->run_t($test)) {
$skip_dirs{$dir} = 1;
@new_tests = grep { m:\Wall\.t$: ||
not $skip_dirs{dirname $_} } @new_tests;
push @new_tests, $test;
}
}
elsif (!$skip_dirs{$dir}) {
push @new_tests, $test;
}
}
@new_tests;
}
sub get_tests {
my $self = shift;
my $args = shift;
my @tests = ();
my $base = -d 't' ? catdir('t', '.') : '.';
my $ts = $args->{tests} || [];
if (@$ts) {
for (@$ts) {
if (-d $_) {
push(@tests, sort <$base/$_/*.t>);
}
else {
$_ .= ".t" unless /\.t$/;
push(@tests, $_);
}
}
}
else {
if ($args->{tdirs}) {
push @tests, map { sort <$base/$_/*.t> } @{ $args->{tdirs} };
}
else {
finddepth(sub {
return unless /\.t$/;
my $t = catfile $File::Find::dir, $_;
my $dotslash = catfile '.', "";
$t =~ s:^\Q$dotslash::;
push @tests, $t
}, $base);
@tests = sort @tests;
}
}
@tests = $self->prune(@tests);
if (my $skip = $self->skip) {
# Allow / \ and \\ path delimiters in SKIP file
$skip =~ s![/\\\\]+![/\\\\]!g;
@tests = grep { not /(?:$skip)/ } @tests;
}
Apache::TestSort->run(\@tests, $args);
#when running 't/TEST t/dir' shell tab completion adds a /
#dir//foo output is annoying, fix that.
s:/+:/:g for @tests;
return @tests;
}
sub run {
my $self = shift;
my $args = shift || {};
$Test::Harness::verbose ||= $args->{verbose};
if (my(@subtests) = @{ $args->{subtests} || [] }) {
$ENV{HTTPD_TEST_SUBTESTS} = "@subtests";
}
Test::Harness::runtests($self->get_tests($args, @_));
}
1;

View file

@ -0,0 +1,139 @@
# 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::TestHarnessPHP;
use strict;
use warnings FATAL => 'all';
use File::Spec::Functions qw(catfile catdir);
use File::Find qw(finddepth);
use Apache::TestHarness ();
use Apache::TestTrace;
use Apache::TestConfig ();
use vars qw(@ISA);
@ISA = qw(Apache::TestHarness);
use TAP::Formatter::Console;
use TAP::Harness;
sub get_tests {
my $self = shift;
my $args = shift;
my @tests = ();
my $base = -d 't' ? catdir('t', '.') : '.';
my $ts = $args->{tests} || [];
if (@$ts) {
for (@$ts) {
if (-d $_) {
push(@tests, sort <$base/$_/*.t>);
push(@tests, sort <$base/$_/*.php>);
}
else {
$_ .= ".t" unless /(\.t|\.php)$/;
push(@tests, $_);
}
}
}
else {
if ($args->{tdirs}) {
push @tests, map { sort <$base/$_/*.t> } @{ $args->{tdirs} };
push @tests, map { sort <$base/$_/*.php> } @{ $args->{tdirs} };
}
else {
finddepth(sub {
return unless /\.(t|php)$/;
return if $File::Find::dir =~ m/\b(conf|htdocs|logs|response)\b/;
my $t = catfile $File::Find::dir, $_;
my $dotslash = catfile '.', "";
$t =~ s:^\Q$dotslash::;
push @tests, $t
}, $base);
@tests = sort @tests;
}
}
@tests = $self->prune(@tests);
if (my $skip = $self->skip) {
# Allow / \ and \\ path delimiters in SKIP file
$skip =~ s![/\\\\]+![/\\\\]!g;
@tests = grep { not /(?:$skip)/ } @tests;
}
Apache::TestSort->run(\@tests, $args);
#when running 't/TEST t/dir' shell tab completion adds a /
#dir//foo output is annoying, fix that.
s:/+:/:g for @tests;
# remove *.php tests unless we can run them with php
if (! Apache::TestConfig::which('php')) {
warning(join ' - ', 'skipping *.php tests',
'make sure php is in your PATH');
@tests = grep { not /\.php$/ } @tests;
}
elsif (! $phpclient) {
warning(join ' - ', 'skipping *.php tests',
'Test::Harness 2.38 not available');
@tests = grep { not /\.php$/ } @tests;
}
return @tests;
}
sub run {
my $self = shift;
my $args = shift || {};
my $formatter = TAP::Formatter::Console->new;
my $agg = TAP::Parser::Aggregator->new;
my $verbose = $args->{verbose} && $args->{verbose};
my $php_harness = TAP::Harness->new
({exec => $self->command_line(),
verbosity => $verbose});
my $perl_harness = TAP::Harness->new
({verbosity => $verbose});
my @tests = $self->get_tests($args, @_);
$agg->start();
$php_harness->aggregate_tests($agg, grep {m{\.php$}} @tests);
$perl_harness->aggregate_tests($agg, grep {m{\.t$}} @tests);
$agg->stop();
$formatter->summary($agg);
}
sub command_line {
my $self = shift;
my $server_root = Apache::Test::vars('serverroot');
my $conf = catfile($server_root, 'conf');
my $ini = catfile($conf, 'php.ini');
my $php = Apache::TestConfig::which('php') ||
die 'no php executable found in ' . $ENV{PATH};
return ["env", "SERVER_ROOT=$server_root",
$php, "--php-ini", $ini, "--define", "include_path=$conf"];
}
1;

View file

@ -0,0 +1,410 @@
# 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::TestMB;
use strict;
use vars qw(@ISA);
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!
print "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.
=item L<http://www.perl.com/pub/a/2003/05/22/testing.html>
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
I<http://perl.apache.org/docs/general/testing/testing.html>.
=cut

View file

@ -0,0 +1,258 @@
# 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;
use Apache::TestSmoke;
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);
Apache::TestSmoke->generate_script;
}
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

View file

@ -0,0 +1,53 @@
# 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.
#
#no 'package Apache::TestPerlDB.pm' here, else we change perldb's package
use strict;
sub Apache::TestPerlDB::lwpd {
print Apache::TestRequest::lwp_debug(shift || 1);
}
sub Apache::TestPerlDB::bok {
my $n = shift || 1;
print "breakpoint set at test $n\n";
DB::cmd_b_sub('ok', "\$Test::ntest == $n");
}
my %help = (
lwpd => 'Set the LWP debug level for Apache::TestRequest',
bok => 'Set breakpoint at test n',
);
my $setup_db_aliases = sub {
my $package = 'Apache::TestPerlDB';
my @cmds;
no strict 'refs';
while (my($name, $val) = each %{"$package\::"}) {
next unless defined &$val;
*{"main::$name"} = \&{$val};
push @cmds, $name;
}
print "$package added perldb commands:\n",
map { " $_ - $help{$_}\n" } @cmds;
};
$setup_db_aliases->();
1;
__END__

View file

@ -0,0 +1,181 @@
# 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::TestReport;
use strict;
use warnings FATAL => 'all';
use Apache::Test ();
use Apache::TestConfig ();
use File::Spec::Functions qw(catfile);
use File::Find;
sub new {
my $class = shift;
bless { @_ }, $class;
}
# generate t/REPORT script (or a different filename) which will drive
# Apache::TestReport
sub generate_script {
my ($class, $file) = @_;
$file ||= catfile 't', 'REPORT';
my $content = join "\n",
"BEGIN { eval { require blib && blib->import; } }",
Apache::TestConfig->perlscript_header,
"use $class;",
"$class->new(\@ARGV)->run;";
Apache::Test::basic_config()->write_perlscript($file, $content);
}
sub replace {
my($self, $template) = @_;
$template =~ s{\@(\w+)\@} {
my $method = lc $1;
eval { $self->$method() } || $self->{$1} || '';
}eg;
$template;
}
sub run {
my $self = shift;
print $self->replace($self->template);
}
sub config { Apache::TestConfig::as_string() }
sub report_to { 'test-dev@httpd.apache.org' }
sub postit_note {
my $self = shift;
my($to, $where) = split '@', $self->report_to;
return <<EOF;
Note: Complete the rest of the details and post this bug report to
$to <at> $where. To subscribe to the list send an empty
email to $to-subscribe\@$where.
EOF
}
sub executable { $0 }
my $core_dump;
sub core_dump {
my $self = shift;
$core_dump = "";
if (eval { require Devel::GDB }) {
find(\&dump_core_file, 't')
}
$core_dump || ' [CORE TRACE COMES HERE]';
}
sub dump_core_file {
return unless /^core(\.\d+)?$/;
my $core = $_;
my $gdb = new Devel::GDB ();
my $test_config = Apache::TestConfig->new({thaw=>1});
my $httpd = $test_config->{vars}->{httpd};
return unless defined $httpd;
$core_dump .= join '',
$gdb->get("file $httpd"),
$gdb->get('sharedlibrary'),
$gdb->get("core $core"),
$gdb->get('info threads'),
$gdb->get('thread apply all bt');
}
sub date { scalar gmtime() . " GMT" }
sub template {
<<'EOI'
-------------8<---------- Start Bug Report ------------8<----------
1. Problem Description:
[DESCRIBE THE PROBLEM HERE]
2. Used Components and their Configuration:
@CONFIG@
3. This is the core dump trace: (if you get a core dump):
@CORE_DUMP@
This report was generated by @EXECUTABLE@ on @DATE@.
-------------8<---------- End Bug Report --------------8<----------
@POSTIT_NOTE@
EOI
}
1;
__END__
=head1 NAME
Apache::TestReport - A parent class for generating bug/success reports
=head1 Synopsis
use Apache::TestReport;
Apache::TestReport->new(@ARGV)->run;
=head1 Description
This class is used to generate a bug or a success report, providing
information about the system the code was running on.
=head1 Overridable Methods
=head2 config
return the information about user's system
=head2 report_to
return a string containing the email address the report should be sent
to
=head2 postit_note
return a string to close the report with, e.g.:
my($to, $where) = split '@', $self->report_to;
return <<EOF;
Note: Complete the rest of the details and post this bug report to
$to <at> $where. To subscribe to the list send an empty
email to $to-subscribe\@$where.
=cut

View file

@ -0,0 +1,40 @@
# 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::TestReportPerl;
use strict;
use warnings FATAL => 'all';
use Apache::TestReport ();
use ModPerl::Config ();
# a subclass of Apache::TestReport that generates a bug report script
use vars qw(@ISA);
@ISA = qw(Apache::TestReport);
sub config {
ModPerl::Config::as_string();
}
sub report_to {
my $self = shift;
my $pkg = ref $self;
die "you need to implement $pkg\::report_to() to return the " .
"contact email address of your project";
}
1;
__END__

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,332 @@
# 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::TestRunPHP;
use strict;
use warnings FATAL => 'all';
use File::Spec::Functions qw(catfile canonpath);
use Apache::TestRun ();
use Apache::TestConfigParse ();
use Apache::TestTrace;
use Apache::TestConfigPHP ();
use Apache::TestHarnessPHP ();
use vars qw($VERSION);
$VERSION = '1.00'; # make CPAN.pm's r() version scanner happy
use File::Spec::Functions qw(catfile);
# subclass of Apache::TestRun that configures php things
use vars qw(@ISA);
@ISA = qw(Apache::TestRun);
sub start {
my $self = shift;
# point php to our own php.ini file
$ENV{PHPRC} = catfile $self->{test_config}->{vars}->{serverroot},
'conf';
$self->SUPER::start(@_);
}
sub new_test_config {
my $self = shift;
Apache::TestConfigPHP->new($self->{conf_opts});
}
sub configure_php {
my $self = shift;
my $test_config = $self->{test_config};
$test_config->postamble_register(qw(configure_php_inc
configure_php_ini
configure_php_functions
configure_php_tests));
}
sub configure {
my $self = shift;
$self->configure_php;
$self->SUPER::configure;
}
#if Apache::TestRun refreshes config in the middle of configure
#we need to re-add php configure hooks
sub refresh {
my $self = shift;
$self->SUPER::refresh;
$self->configure_php;
}
my @request_opts = qw(get post head);
sub run_tests {
my $self = shift;
my $test_opts = {
verbose => $self->{opts}->{verbose},
tests => $self->{tests},
order => $self->{opts}->{order},
subtests => $self->{subtests} || [],
};
if (grep { exists $self->{opts}->{$_} } @request_opts) {
run_request($self->{test_config}, $self->{opts});
}
else {
Apache::TestHarnessPHP->run($test_opts)
if $self->{opts}->{'run-tests'};
}
}
sub split_test_args {
my($self) = @_;
my(@tests);
my $top_dir = $self->{test_config}->{vars}->{top_dir};
my $t_dir = $self->{test_config}->{vars}->{t_dir};
my $argv = $self->{argv};
my @leftovers = ();
for (@$argv) {
my $arg = $_;
# need the t/ (or t\) for stat-ing, but don't want to include
# it in test output
$arg =~ s@^(?:\.[\\/])?t[\\/]@@;
my $file = catfile $t_dir, $arg;
if (-d $file and $_ ne '/') {
my @files = <$file/*.t>;
push @files, <$file/*.php>;
my $remove = catfile $top_dir, "";
if (@files) {
push @tests, map { s,^\Q$remove,,; $_ } @files;
next;
}
}
else {
if (($file =~ /\.t$/ || $file =~ /\.php$/) and -e $file) {
push @tests, "t/$arg";
next;
}
elsif (-e "$file.t") {
push @tests, "t/$arg.t";
next;
}
elsif (/^[\d.]+$/) {
my @t = $_;
#support range of subtests: t/TEST t/foo/bar 60..65
if (/^(\d+)\.\.(\d+)$/) {
@t = $1..$2;
}
push @{ $self->{subtests} }, @t;
next;
}
}
push @leftovers, $_;
}
$self->{tests} = [ map { canonpath($_) } @tests ];
$self->{argv} = \@leftovers;
}
1;
__END__
=head1 NAME
Apache::TestRunPHP - configure and run a PHP-based test suite
=head1 SYNOPSIS
use Apache::TestRunPHP;
Apache::TestRunPHP->new->run(@ARGV);
=head1 DESCRIPTION
The C<Apache::TestRunPHP> package controls the configuration and
running of the test suite for PHP-based tests. It's a subclass
of C<Apache::TestRun> and similar in function to C<Apache::TestRunPerl>.
Refer to the C<Apache::TestRun> manpage for information on the
available API.
=head1 EXAMPLE
C<TestRunPHP> works almost identially to C<TestRunPerl>, but in
case you are new to C<Apache-Test> here is a quick getting started
guide. be sure to see the links at the end of this document for
places to find additional details.
because C<Apache-Test> is a Perl-based testing framework we start
from a C<Makefile.PL>, which should have the following lines (in
addition to the standard C<Makefile.PL> parts):
use Apache::TestMM qw(test clean);
use Apache::TestRunPHP ();
Apache::TestMM::filter_args();
Apache::TestRunPHP->generate_script();
C<generate_script()> will create a script named C<t/TEST>, the gateway
to the Perl testing harness and what is invoked when you call
C<make test>. C<filter_args()> accepts some C<Apache::Test>-specific
arguments and passes them along. for example, to point to a specific
C<httpd> installation you would invoke C<Makefile.PL> as follows
$ perl Makefile.PL -httpd /my/local/apache/bin/httpd
and C</my/local/apache/bin/httpd> will be propagated throughout the
rest of the process. note that PHP needs to be active within Apache
prior to configuring the test framework as shown above, either by
virtue of PHP being compiled into the C<httpd> binary statically or
through an active C<LoadModule> statement within the configuration
located in C</my/local/apache/conf/httpd.conf>. Other required modules
are the (very common) mod_alias and mod_env.
now, like with C<Apache::TestRun> and C<Apache::TestRunPerl>, you can
place client-side Perl test scripts under C<t/>, such as C<t/01basic.t>,
and C<Apache-Test> will run these scripts when you call C<make test>.
however, what makes C<Apache::TestRunPHP> unique is some added magic
specifically tailored to a PHP environment. here are the mechanics.
C<Apache::TestRunPHP> will look for PHP test scripts in that match
the following pattern
t/response/TestFoo/bar.php
where C<Foo> and C<bar> can be anything you like, and C<t/response/Test*>
is case sensitive. when this format is adhered to, C<Apache::TestRunPHP>
will create an associated Perl test script called C<t/foo/bar.t>, which
will be executed when you call C<make test>. all C<bar.t> does is issue
a simple GET to C<bar.php>, leaving the actual testing to C<bar.php>. in
essence, you can forget that C<bar.t> even exists.
what does C<bar.php> look like? here is an example:
<?php
print "1..1\n";
print "ok 1\n"
?>
if it looks odd, that's ok because it is. I could explain to you exactly
what this means, but it isn't important to understand the gory details.
instead, it is sufficient to understand that when C<Apache::Test> calls
C<bar.php> it feeds the results directly to C<Test::Harness>, a module
that comes with every Perl installation, and C<Test::Harness> expects
what it receives to be formated in a very specific way. by itself, all
of this is pretty useless, so C<Apache::Test> provides PHP testers with
something much better. here is a much better example:
<?php
# import the Test::More emulation layer
# see
# http://search.cpan.org/dist/Test-Simple/lib/Test/More.pm
# for Perl's documentation - these functions should behave
# in the same way
require 'test-more.php';
# plan() the number of tests
plan(6);
# call ok() for each test you plan
ok ('foo' == 'foo', 'foo is equal to foo');
ok ('foo' != 'foo', 'foo is not equal to foo');
# ok() can be other things as well
is ('bar', 'bar', 'bar is bar');
is ('baz', 'bar', 'baz is baz');
isnt ('bar', 'beer', 'bar is not beer');
like ('bar', '/ar$/', 'bar matches ar$');
diag("printing some debugging information");
# whoops! one too many tests. I wonder what will happen...
is ('biff', 'biff', 'baz is a baz');
?>
the include library C<test-more.php> is automatically generated by
C<Apache::TestConfigPHP> and configurations tweaked in such a
a way that your PHP scripts can find it without issue. the
functions provided by C<test-more.php> are equivalent in name and
function to those in C<Test::More>, a standard Perl testing
library, so you can see that manpage for details on the syntax
and functionality of each.
at this point, we have enough in place to run some tests from
PHP-land - a C<Makefile.PL> to configure Apache for us, and
a PHP script in C<t/response/TestFoo/bar.php> to send some
results out to the testing engine. issuing C<make test>
would start Apache, issue the request to C<bar.php>, generate
a report, and shut down Apache. the report would look like
something like this after running the tests in verbose mode
(eg C<make test TEST_VERBOSE=1>):
t/php/bar....1..6
ok 1 - foo is equal to foo
not ok 2 - foo is not equal to foo
# Failed test (/src/devel/perl-php-test/t/response/TestFoo/bar.php at line 13)
ok 3 - bar is bar
not ok 4 - baz is baz
# Failed test (/src/devel/perl-php-test/t/response/TestFoo/bar.php at line 17)
# got: 'baz'
# expected: 'bar'
ok 5 - bar is not beer
ok 6 - bar matches ar$
# printing some debugging information
ok 7 - baz is a baz
FAILED tests 2, 4, 7
Failed 3/6 tests, 50.00% okay
Failed Test Stat Wstat Total Fail Failed List of Failed
-------------------------------------------------------------------------------
t/php/bar.t 6 3 50.00% 2 4 7
Failed 1/1 test scripts, 0.00% okay. 1/6 subtests failed, 83.33% okay.
note that the actual test file that was run was C<t/php/bar.t>. this
file is autogenerated based on the C<t/response/TestFoo/bar.php>
pattern of your PHP script. C<t/php/bar.t> happens to be written in
Perl, but you really don't need to worry about it too much.
as an interesting aside, if you are using perl-5.8.3 or later you can
actually create your own C<t/foo.php> client-side scripts and they
will be run via php (using our C<php.ini>). but more on that later...
=head1 SEE ALSO
the best source of information about using Apache-Test with
PHP (at this time) is probably the talk given at ApacheCon 2004
(L<http://xrl.us/phpperl>), as well as the code from the talk
(L<http://xrl.us/phpperlcode>). there is also the online tutorial
L<http://perl.apache.org/docs/general/testing/testing.html>
which has all of the mod_perl-specific syntax and features have been
ported to PHP with this class.
=head1 AUTHOR
C<Apache-Test> is a community effort, maintained by a group of
dedicated volunteers.
Questions can be asked at the test-dev <at> httpd.apache.org list
For more information see: http://httpd.apache.org/test/.
=cut

View file

@ -0,0 +1,68 @@
# 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::TestRunParrot;
use strict;
use warnings FATAL => 'all';
use File::Spec::Functions qw(catfile canonpath);
use Apache::TestRun ();
use Apache::TestConfigParse ();
use Apache::TestTrace;
use Apache::TestConfigParrot ();
use vars qw($VERSION);
$VERSION = '1.00'; # make CPAN.pm's r() version scanner happy
use File::Spec::Functions qw(catfile);
# subclass of Apache::TestRun that configures parrot things
use vars qw(@ISA);
@ISA = qw(Apache::TestRun);
sub new_test_config {
my $self = shift;
Apache::TestConfigParrot->new($self->{conf_opts});
}
sub configure_parrot {
my $self = shift;
my $test_config = $self->{test_config};
$test_config->postamble_register(qw(configure_parrot_tests));
}
sub configure {
my $self = shift;
$self->configure_parrot;
$self->SUPER::configure;
}
#if Apache::TestRun refreshes config in the middle of configure
#we need to re-add parrotconfigure hooks
sub refresh {
my $self = shift;
$self->SUPER::refresh;
$self->configure_parrot;
}
1;
__END__

View file

@ -0,0 +1,139 @@
# 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::TestRunPerl;
use strict;
use warnings FATAL => 'all';
use Apache::TestRun ();
use Apache::TestConfigParse ();
use Apache::TestTrace;
use vars qw($VERSION);
$VERSION = '1.00'; # make CPAN.pm's r() version scanner happy
use File::Spec::Functions qw(catfile);
#subclass of Apache::TestRun that configures mod_perlish things
use vars qw(@ISA);
@ISA = qw(Apache::TestRun);
sub pre_configure {
my $self = shift;
# Apache::TestConfigPerl already configures mod_perl.so
Apache::TestConfig::autoconfig_skip_module_add('mod_perl.c');
# skip over Embperl.so - it's funky
Apache::TestConfig::autoconfig_skip_module_add('Embperl.c');
}
sub configure_modperl {
my $self = shift;
my $test_config = $self->{test_config};
my $rev = $test_config->server->{rev};
my $ver = $test_config->server->{version};
# sanity checking and loading the right mod_perl version
# remove mod_perl.pm from %INC so that the below require()
# calls accurately populate $mp_ver
delete $INC{'mod_perl.pm'};
if ($rev == 2) {
eval { require mod_perl2 };
} else {
eval { require mod_perl };
}
my $mp_ver = $mod_perl::VERSION;
if ($@) {
error "You are using mod_perl response handlers ",
"but do not have a mod_perl capable Apache.";
Apache::TestRun::exit_perl(0);
}
if (($rev == 1 && $mp_ver >= 1.99) ||
($rev == 2 && $mp_ver < 1.99)) {
error "Found mod_perl/$mp_ver, but it can't be used with $ver";
Apache::TestRun::exit_perl(0);
}
if ($rev == 2) {
# load apreq2 if it is present
# do things a bit differently that find_and_load_module()
# because apreq2 can't be loaded that way (the 2 causes a problem)
my $name = 'mod_apreq2.so';
if (my $mod_path = $test_config->find_apache_module($name)) {
# don't match the 2 here
my ($sym) = $name =~ m/mod_(\w+)2\./;
if ($mod_path && -e $mod_path) {
$test_config->preamble(IfModule => "!mod_$sym.c",
qq{LoadModule ${sym}_module "$mod_path"\n});
}
}
}
$test_config->preamble_register(qw(configure_libmodperl
configure_env));
$test_config->postamble_register(qw(configure_inc
configure_pm_tests_inc
configure_startup_pl
configure_pm_tests));
}
sub configure {
my $self = shift;
$self->configure_modperl;
$self->SUPER::configure;
}
#if Apache::TestRun refreshes config in the middle of configure
#we need to re-add modperl configure hooks
sub refresh {
my $self = shift;
$self->SUPER::refresh;
$self->configure_modperl;
}
1;
__END__
=head1 NAME
Apache::TestRunPerl - Run mod_perl-requiring Test Suite
=head1 SYNOPSIS
use Apache::TestRunPerl;
Apache::TestRunPerl->new->run(@ARGV);
=head1 DESCRIPTION
The C<Apache::TestRunPerl> package controls the configuration and
running of the test suite. It's a subclass of C<Apache::TestRun>, and
should be used only when you need to run mod_perl tests.
Refer to the C<Apache::TestRun> manpage for information on the
available API.
=cut

View file

@ -0,0 +1,608 @@
# 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::TestSSLCA;
use strict;
use warnings FATAL => 'all';
use Cwd ();
use DirHandle ();
use File::Path ();
use File::Copy 'cp';
use File::Basename;
use File::Spec::Functions qw(devnull);
use Apache::TestConfig ();
use Apache::TestTrace;
use constant SSLCA_DB => 'index.txt';
use vars qw(@EXPORT_OK &import);
use subs qw(symlink);
@EXPORT_OK = qw(dn dn_vars dn_oneline);
*import = \&Exporter::import;
my $openssl = $ENV{APACHE_TEST_OPENSSL_CMD} || 'openssl';
my $version = version();
my $CA = 'asf';
my $Config; #global Apache::TestConfig object
my $days = '-days 365';
my $cakey = 'keys/ca.pem';
my $cacert = 'certs/ca.crt';
my $capolicy = '-policy policy_anything';
my $cacrl = 'crl/ca-bundle.crl';
my $dgst = 'sha256';
#we use the same password for everything
my $pass = 'httpd';
my $passin = "-passin pass:$pass";
my $passout = "-passout pass:$pass";
# (limited) subjectAltName otherName testing
my $san_msupn = ', otherName:msUPN;UTF8:$mail';
my $san_dnssrv = ', otherName:1.3.6.1.5.5.7.8.7;IA5:_https.$CN';
# in 0.9.7 s/Email/emailAddress/ in DN
my $email_field = Apache::Test::normalize_vstring($version) <
Apache::Test::normalize_vstring("0.9.7") ?
"Email" : "emailAddress";
# downgrade to SHA-1 for OpenSSL before 0.9.8
if (Apache::Test::normalize_vstring($version) <
Apache::Test::normalize_vstring("0.9.8")) {
$dgst = 'sha1';
# otherNames in x509v3_config are not supported either
$san_msupn = $san_dnssrv = "";
}
my $sslproto = "all";
eval { require Net::SSLeay; };
if (Apache::Test::normalize_vstring($version) >=
Apache::Test::normalize_vstring("1.1.1")
&& !defined(&Net::SSLeay::CTX_set_post_handshake_auth)) {
# OpenSSL 1.1.1 disables PHA by default client-side in TLSv1.3 but
# most clients are not updated to enable it (at time of writing).
# Many mod_ssl tests require working PHA, so disable v1.3 unless
# using an updated Net::SSLeay. This is strictly insufficient
# since an updated IO::Socket::SSL is also needed; to be
# continued. Ref: https://github.com/openssl/openssl/issues/6933
$sslproto = "all -TLSv1.3";
}
my $ca_dn = {
asf => {
C => 'US',
ST => 'California',
L => 'San Francisco',
O => 'ASF',
OU => 'httpd-test',
CN => '',
$email_field => 'test-dev@httpd.apache.org',
},
};
my $cert_dn = {
client_snakeoil => {
C => 'AU',
ST => 'Queensland',
L => 'Mackay',
O => 'Snake Oil, Ltd.',
OU => 'Staff',
},
client_ok => {
},
client_colon => {
CN => "user:colon",
},
client_revoked => {
},
server => {
CN => 'localhost',
OU => 'httpd-test/rsa-test',
},
server2 => {
CN => 'localhost',
OU => 'httpd-test/rsa-test-2',
},
server_des3 => {
CN => 'localhost',
OU => 'httpd-test/rsa-des3-test',
},
server2_des3 => {
CN => 'localhost',
OU => 'httpd-test/rsa-des3-test-2',
},
};
#generate DSA versions of the server certs/keys
for my $key (keys %$cert_dn) {
next unless $key =~ /^server/;
my $val = $$cert_dn{$key};
my $name = join '_', $key, 'dsa';
$cert_dn->{$name} = { %$val }; #copy
$cert_dn->{$name}->{OU} =~ s/rsa/dsa/;
}
sub ca_dn {
$ca_dn = shift if @_;
$ca_dn;
}
sub cert_dn {
$cert_dn = shift if @_;
$cert_dn;
}
sub dn {
my $name = shift;
my %dn = %{ $ca_dn->{$CA} }; #default values
$dn{CN} ||= $name; #try make sure each Common Name is different
my $default_dn = $cert_dn->{$name};
if ($default_dn) {
while (my($key, $value) = each %$default_dn) {
#override values
$dn{$key} = $value;
}
}
return wantarray ? %dn : \%dn;
}
sub dn_vars {
my($name, $type) = @_;
my $dn = dn($name);
my $prefix = join '_', 'SSL', $type, 'DN';
return { map { $prefix ."_$_", $dn->{$_} } keys %$dn };
}
sub dn_oneline {
my($dn, $rfc2253) = @_;
unless (ref $dn) {
$dn = dn($dn);
}
my $string = "";
my @parts = (qw(C ST L O OU CN), $email_field);
@parts = reverse @parts if $rfc2253;
for my $k (@parts) {
next unless $dn->{$k};
if ($rfc2253) {
my $tmp = $dn->{$k};
$tmp =~ s{([,+"\\<>;])}{\\$1}g;
$tmp =~ s{^([ #])}{\\$1};
$tmp =~ s{ $}{\\ };
$string .= "," if $string;
$string .= "$k=$tmp";
}
else {
$string .= "/$k=$dn->{$k}";
}
}
$string;
}
sub openssl {
return $openssl unless @_;
my $cmd = "$openssl @_";
info $cmd;
unless (system($cmd) == 0) {
my $status = $? >> 8;
die "system @_ failed (exit status=$status)";
}
}
my @dirs = qw(keys newcerts certs crl export csr conf proxy);
sub init {
for my $dir (@dirs) {
gendir($dir);
}
}
sub config_file {
my $name = shift;
my $file = "conf/$name.cnf";
return $file if -e $file;
my $dn = dn($name);
my $db = SSLCA_DB;
writefile($db, '', 1) unless -e $db;
writefile($file, <<EOF);
mail = $dn->{$email_field}
CN = $dn->{CN}
[ req ]
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
default_bits = 2048
output_password = $pass
[ req_distinguished_name ]
C = $dn->{C}
ST = $dn->{ST}
L = $dn->{L}
O = $dn->{O}
OU = $dn->{OU}
CN = \$CN
$email_field = \$mail
[ req_attributes ]
challengePassword = $pass
[ ca ]
default_ca = CA_default
[ CA_default ]
certs = certs # Where the issued certs are kept
new_certs_dir = newcerts # default place for new certs.
crl_dir = crl # Where the issued crl are kept
database = $db # database index file.
serial = serial # The current serial number
certificate = $cacert # The CA certificate
crl = $cacrl # The current CRL
private_key = $cakey # The private key
default_days = 365 # how long to certify for
default_crl_days = 365 # how long before next CRL
default_md = $dgst # which md to use.
preserve = no # keep passed DN ordering
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
$email_field = optional
[ client_ok_ext ]
nsComment = This Is A Comment
1.3.6.1.4.1.18060.12.0 = DER:0c064c656d6f6e73
subjectAltName = email:\$mail$san_msupn
[ client_ext ]
extendedKeyUsage = clientAuth
[ server_ext ]
subjectAltName = DNS:\$CN$san_dnssrv
extendedKeyUsage = serverAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
[ ca_ext ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
EOF
return $file;
}
sub config {
my $name = shift;
my $file = config_file($name);
my $config = "-config $file";
$config;
}
use constant PASSWORD_CLEARTEXT =>
Apache::TestConfig::WIN32 || Apache::TestConfig::NETWARE;
#http://www.modssl.org/docs/2.8/ssl_reference.html#ToC21
my $basic_auth_password =
PASSWORD_CLEARTEXT ? 'password': 'xxj31ZMTZzkVA';
my $digest_auth_hash = '$1$OXLyS...$Owx8s2/m9/gfkcRVXzgoE/';
sub new_ca {
writefile('serial', "01\n", 1);
writefile('ssl.htpasswd',
join ':', dn_oneline('client_snakeoil'),
$basic_auth_password);
openssl req => "-new -x509 -extensions ca_ext -keyout $cakey -out $cacert $days",
config('ca');
export_cert('ca'); #useful for importing into IE
}
sub new_key {
my $name = shift;
my $encrypt = @_ ? "@_ $passout" : "";
my $out = "-out keys/$name.pem $encrypt";
if ($name =~ /dsa/) {
#this takes a long time so just do it once
#don't do this in real life
unless (-e 'dsa-param') {
openssl dsaparam => '-inform PEM -out dsa-param 2048';
}
openssl gendsa => "$out dsa-param";
}
else {
openssl genrsa => "$out 2048";
}
}
sub new_cert {
my $name = shift;
openssl req => "-new -key keys/$name.pem -out csr/$name.csr",
$passin, $passout, config($name);
sign_cert($name);
export_cert($name);
}
sub sign_cert {
my $name = shift;
my $exts = '';
$exts = ' -extensions client_ext' if $name =~ /client/;
$exts .= ' -extensions client_ok_ext' if $name =~ /client_ok/;
$exts = ' -extensions server_ext' if $name =~ /server/;
openssl ca => "$capolicy -in csr/$name.csr -out certs/$name.crt",
$passin, config($name), '-batch', $exts;
}
#handy for importing into a browser such as netscape
sub export_cert {
my $name = shift;
return if $name =~ /^server/; #no point in exporting server certs
openssl pkcs12 => "-export -in certs/$name.crt -inkey keys/$name.pem",
"-out export/$name.p12", $passin, $passout;
}
sub revoke_cert {
my $name = shift;
my @args = (config('cacrl'), $passin);
#revokes in the SSLCA_DB database
openssl ca => "-revoke certs/$name.crt", @args;
#generates crl from the index.txt database
openssl ca => "-gencrl -out $cacrl", @args;
}
sub symlink {
my($file, $symlink) = @_;
my $what = 'linked';
if (Apache::TestConfig::WINFU) {
cp $file, $symlink;
$what = 'copied';
}
else {
CORE::symlink($file, $symlink);
}
info "$what $file to $symlink";
}
sub hash_certs {
my($type, $dir) = @_;
chdir $dir;
my $dh = DirHandle->new('.') or die "opendir $dir: $!";
my $n = 0;
for my $file ($dh->read) {
next unless $file =~ /\.cr[tl]$/;
chomp(my $hash = `$openssl $type -noout -hash < $file`);
next unless $hash;
my $symlink = "$hash.r$n";
$n++;
symlink $file, $symlink;
}
close $dh;
chdir $CA;
}
sub make_proxy_cert {
my $name = shift;
my $from = "certs/$name.crt";
my $to = "proxy/$name.pem";
info "generating proxy cert: $to";
my $fh_to = Symbol::gensym();
my $fh_from = Symbol::gensym();
open $fh_to, ">$to" or die "open $to: $!";
open $fh_from, $from or die "open $from: $!";
cp $fh_from, $fh_to;
$from = "keys/$name.pem";
open $fh_from, $from or die "open $from: $!";
cp $fh_from, $fh_to;
close $fh_from;
close $fh_to;
}
sub setup {
$CA = shift;
unless ($ca_dn->{$CA}) {
die "unknown CA $CA";
}
gendir($CA);
chdir $CA;
init();
new_ca();
my @names = keys %$cert_dn;
for my $name (@names) {
my @key_args = ();
if ($name =~ /_des3/) {
push @key_args, '-des3';
}
new_key($name, @key_args);
new_cert($name);
if ($name =~ /_revoked$/) {
revoke_cert($name);
}
if ($name =~ /^client_/) {
make_proxy_cert($name);
}
}
hash_certs(crl => 'crl');
}
sub generate {
$Config = shift;
$CA = shift || $Config->{vars}->{sslcaorg};
my $root = $Config->{vars}->{sslca};
return if -d $root;
my $pwd = Cwd::cwd();
my $base = dirname $root;
my $dir = basename $root;
chdir $base;
# Ensure the CNs used in the server certs match up with the
# hostname being used for testing.
while (my($key, $val) = each %$cert_dn) {
next unless $key =~ /^server/;
$val->{CN} = $Config->{vars}->{servername};
}
#make a note that we created the tree
$Config->clean_add_path($root);
gendir($dir);
chdir $dir;
warning "generating SSL CA for $CA";
setup($CA);
chdir $pwd;
}
sub clean {
my $config = shift;
#rel2abs adds same drive letter for win32 that clean_add_path added
my $dir = File::Spec->rel2abs($config->{vars}->{sslca});
unless ($config->{clean}->{dirs}->{$dir}) {
return; #we did not generate this ca
}
unless ($config->{clean_level} > 1) {
#skip t/TEST -conf
warning "skipping regeneration of SSL CA; run t/TEST -clean to force";
return;
}
File::Path::rmtree([$dir], 1, 1);
}
#not using Apache::TestConfig methods because the openssl commands
#will generate heaps of files we cannot keep track of
sub writefile {
my($file, $content) = @_;
my $fh = Symbol::gensym();
open $fh, ">$file" or die "open $file: $!";
print $fh $content;
close $fh;
}
sub gendir {
my($dir) = @_;
return if -d $dir;
mkdir $dir, 0755;
}
sub version {
my $devnull = devnull();
my $version = qx($openssl version 2>$devnull);
return $1 if $version =~ /^\S+SSL (\S+)/;
die "FATAL: unable to determine openssl version via `$openssl version` from: $version";
}
sub dgst {
return $dgst;
}
sub email_field {
return $email_field;
}
sub sslproto {
return $sslproto;
}
1;
__END__

View file

@ -0,0 +1,724 @@
# 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::TestServer;
use strict;
use warnings FATAL => 'all';
use Config;
use Socket ();
use File::Spec::Functions qw(catfile);
use Apache::TestTrace;
use Apache::TestRun;
use Apache::TestConfig ();
use Apache::TestRequest ();
use constant COLOR => Apache::TestConfig::COLOR;
use constant WIN32 => Apache::TestConfig::WIN32;
my $CTRL_M = COLOR ? "\r" : "\n";
# some debuggers use the same syntax as others, so we reuse the same
# code by using the following mapping
my %debuggers = (
gdb => 'gdb',
ddd => 'gdb',
valgrind => 'valgrind',
strace => 'strace',
);
sub new {
my $class = shift;
my $config = shift;
my $self = bless {
config => $config || Apache::TestConfig->thaw,
}, $class;
$self->{name} = join ':',
map { $self->{config}->{vars}->{$_} } qw(servername port);
$self->{port_counter} = $self->{config}->{vars}->{port};
$self;
}
# call this when you already know where httpd is
sub post_config {
my($self) = @_;
$self->{version} = $self->{config}->httpd_version || '';
$self->{mpm} = $self->{config}->httpd_mpm || '';
# try to get the revision number from the standard Apache version
# string and various variations made by distributions which mangle
# that string
# Foo-Apache-Bar/x.y.z
($self->{rev}) = $self->{version} =~ m|/(\d)\.|;
if ($self->{rev}) {
debug "Matched Apache revision $self->{version} $self->{rev}";
}
else {
# guessing is not good as it'll only mislead users
# and we can't die since a config object is required
# during Makefile.PL's write_perlscript when path to httpd may
# be unknown yet. so default to non-existing version 0 for now.
# and let TestRun.pm figure out the required pieces
debug "can't figure out Apache revision, from string: " .
"'$self->{version}', using a non-existing revision 0";
$self->{rev} = 0; # unknown
}
($self->{revminor}) = $self->{version} =~ m|/\d\.(\d)|;
if ($self->{revminor}) {
debug "Matched Apache revminor $self->{version} $self->{revminor}";
}
else {
$self->{revminor} = 0;
}
$self;
}
sub version_of {
my($self, $thing) = @_;
die "Can't figure out what Apache server generation we are running"
unless $self->{rev};
$thing->{$self->{rev}};
}
my @apache_logs = qw(
error_log access_log httpd.pid
apache_runtime_status rewrite_log
ssl_engine_log ssl_request_log
cgisock
);
sub clean {
my $self = shift;
my $dir = $self->{config}->{vars}->{t_logs};
for (@apache_logs) {
my $file = catfile $dir, $_;
if (unlink $file) {
debug "unlink $file";
}
}
}
sub pid_file {
my $self = shift;
my $vars = $self->{config}->{vars};
return $vars->{t_pid_file} || catfile $vars->{t_logs}, 'httpd.pid';
}
sub dversion {
my $self = shift;
my $dv = "-D APACHE$self->{rev}";
if ($self->{rev} == 2 and $self->{revminor} == 4) {
$dv .= " -D APACHE2_4";
}
return $dv;
}
sub config_defines {
my $self = shift;
my @defines = ();
for my $item (qw(useithreads)) {
next unless $Config{$item} and $Config{$item} eq 'define';
push @defines, "-D PERL_\U$item";
}
if (my $defines = $self->{config}->{vars}->{defines}) {
push @defines, map { "-D $_" } split " ", $defines;
}
"@defines";
}
sub args {
my $self = shift;
my $vars = $self->{config}->{vars};
my $dversion = $self->dversion; #for .conf version conditionals
my $defines = $self->config_defines;
"-d $vars->{serverroot} -f $vars->{t_conf_file} $dversion $defines";
}
my %one_process = (1 => '-X', 2 => '-D ONE_PROCESS');
sub start_cmd {
my $self = shift;
my $args = $self->args;
my $config = $self->{config};
my $vars = $config->{vars};
my $httpd = $vars->{httpd};
my $one_process = $self->{run}->{opts}->{'one-process'}
? $self->version_of(\%one_process)
: '';
#XXX: threaded mpm does not respond to SIGTERM with -D ONE_PROCESS
return "$httpd $one_process $args";
}
sub default_gdbinit {
my $gdbinit = "";
my @sigs = qw(PIPE);
for my $sig (@sigs) {
for my $flag (qw(pass nostop)) {
$gdbinit .= "handle SIG$sig $flag\n";
}
}
$gdbinit;
}
sub strace_cmd {
my($self, $strace, $file) = @_;
#XXX truss, ktrace, etc.
"$strace -f -o $file -s1024";
}
sub valgrind_cmd {
my($self, $valgrind) = @_;
"$valgrind -v --leak-check=yes --show-reachable=yes --error-limit=no";
}
sub start_valgrind {
my $self = shift;
my $opts = shift;
my $config = $self->{config};
my $args = $self->args;
my $one_process = $self->version_of(\%one_process);
my $valgrind_cmd = $self->valgrind_cmd($opts->{debugger});
my $httpd = $config->{vars}->{httpd};
my $command = "$valgrind_cmd $httpd $one_process $args";
debug $command;
system $command;
}
sub start_strace {
my $self = shift;
my $opts = shift;
my $config = $self->{config};
my $args = $self->args;
my $one_process = $self->version_of(\%one_process);
my $file = catfile $config->{vars}->{t_logs}, 'strace.log';
my $strace_cmd = $self->strace_cmd($opts->{debugger}, $file);
my $httpd = $config->{vars}->{httpd};
$config->genfile($file); #just mark for cleanup
my $command = "$strace_cmd $httpd $one_process $args";
debug $command;
system $command;
}
sub start_gdb {
my $self = shift;
my $opts = shift;
my $debugger = $opts->{debugger};
my @breakpoints = @{ $opts->{breakpoint} || [] };
my $config = $self->{config};
my $args = $self->args;
my $one_process = $self->version_of(\%one_process);
my $file = catfile $config->{vars}->{serverroot}, '.gdb-test-start';
my $fh = $config->genfile($file);
print $fh default_gdbinit();
if (@breakpoints) {
print $fh "b ap_run_pre_config\n";
print $fh "run $one_process $args\n";
print $fh "finish\n";
for (@breakpoints) {
print $fh "b $_\n"
}
print $fh "continue\n";
}
else {
print $fh "run $one_process $args\n";
}
close $fh;
my $command;
my $httpd = $config->{vars}->{httpd};
if ($debugger eq 'ddd') {
$command = qq{ddd --gdb --debugger "gdb -command $file" $httpd};
}
else {
## defaults to gdb if not set in %ENV or via -debug
$command = "$debugger $httpd -command $file";
}
$self->note_debugging;
debug $command;
system $command;
unlink $file;
}
sub debugger_file {
my $self = shift;
catfile $self->{config}->{vars}->{serverroot}, '.debugging';
}
#make a note that the server is running under the debugger
#remove note when this process exits via END
sub note_debugging {
my $self = shift;
my $file = $self->debugger_file;
my $fh = $self->{config}->genfile($file);
eval qq(END { unlink "$file" });
}
sub start_debugger {
my $self = shift;
my $opts = shift;
$opts->{debugger} ||= $ENV{MP_DEBUGGER} || 'gdb';
# XXX: FreeBSD 5.2+
# gdb 6.1 and before segfaults when trying to
# debug httpd startup code. 6.5 has been proven
# to work. FreeBSD typically installs this as
# gdb65.
# Is it worth it to check the debugger and os version
# and die ?
unless (grep { /^$opts->{debugger}/ } keys %debuggers) {
error "$opts->{debugger} is not a supported debugger",
"These are the supported debuggers: ".
join ", ", sort keys %debuggers;
die("\n");
}
my $debugger = $opts->{debugger};
$debugger =~ s/\d+$//;
my $method = "start_" . $debuggers{$debugger};
## $opts->{debugger} is passed through unchanged
## so when we try to run it next, its found.
$self->$method($opts);
}
sub pid {
my $self = shift;
my $file = $self->pid_file;
my $fh = Symbol::gensym();
open $fh, $file or do {
return 0;
};
# try to avoid the race condition when the pid file was created
# but not yet written to
for (1..8) {
last if -s $file > 0;
select undef, undef, undef, 0.25;
}
chomp(my $pid = <$fh> || '');
$pid;
}
sub select_next_port {
my $self = shift;
my $max_tries = 100; #XXX
while ($max_tries-- > 0) {
return $self->{port_counter}
if $self->port_available(++$self->{port_counter});
}
return 0;
}
sub port_available {
my $self = shift;
my $port = shift || $self->{config}->{vars}->{port};
local *S;
my $proto = getprotobyname('tcp');
socket(S, Socket::PF_INET(),
Socket::SOCK_STREAM(), $proto) || die "socket: $!";
setsockopt(S, Socket::SOL_SOCKET(),
Socket::SO_REUSEADDR(),
pack("l", 1)) || die "setsockopt: $!";
if (bind(S, Socket::sockaddr_in($port, Socket::INADDR_ANY()))) {
close S;
return 1;
}
else {
return 0;
}
}
=head2 stop()
attempt to stop the server.
returns:
on success: $pid of the server
on failure: -1
=cut
sub stop {
my $self = shift;
my $aborted = shift;
if (WIN32) {
require Win32::Process;
my $obj = $self->{config}->{win32obj};
my $pid = -1;
if ($pid = $obj ? $obj->GetProcessID : $self->pid) {
if (kill(0, $pid)) {
Win32::Process::KillProcess($pid, 0);
warning "server $self->{name} shutdown";
}
}
unlink $self->pid_file if -e $self->pid_file;
return $pid;
}
my $pid = 0;
my $tries = 3;
my $tried_kill = 0;
my $port = $self->{config}->{vars}->{port};
while ($self->ping) {
#my $state = $tried_kill ? "still" : "already";
#print "Port $port $state in use\n";
if ($pid = $self->pid and !$tried_kill++) {
if (kill TERM => $pid) {
warning "server $self->{name} shutdown";
sleep 1;
for (1..6) {
if (! $self->ping) {
if ($_ == 1) {
unlink $self->pid_file if -e $self->pid_file;
return $pid;
}
last;
}
if ($_ == 1) {
warning "port $port still in use...";
}
else {
print "...";
}
sleep $_;
}
if ($self->ping) {
error "\nserver was shutdown but port $port ".
"is still in use, please shutdown the service ".
"using this port or select another port ".
"for the tests";
}
else {
print "done\n";
}
}
else {
error "kill $pid failed: $!";
}
}
else {
error "port $port is in use, ".
"cannot determine server pid to shutdown";
return -1;
}
if (--$tries <= 0) {
error "cannot shutdown server on Port $port, ".
"please shutdown manually";
unlink $self->pid_file if -e $self->pid_file;
return -1;
}
}
unlink $self->pid_file if -e $self->pid_file;
return $pid;
}
sub ping {
my $self = shift;
my $pid = $self->pid;
if ($pid and kill 0, $pid) {
return $pid;
}
elsif (! $self->port_available) {
return -1;
}
return 0;
}
sub failed_msg {
my $self = shift;
my($log, $rlog) = $self->{config}->error_log;
my $log_file_info = -e $log ?
"please examine $rlog" :
"$rlog wasn't created, start the server in the debug mode";
error "@_ ($log_file_info)";
}
#this doesn't work well on solaris or hpux at the moment
use constant USE_SIGCHLD => $^O eq 'linux';
sub start {
my $self = shift;
my $old_pid = -1;
if (WIN32) {
# Stale PID files (e.g. left behind from a previous test run
# that crashed) cannot be trusted on Windows because PID's are
# re-used too frequently, so just remove it. If there is an old
# server still running then the attempt to start a new one below
# will simply fail because the port will be unavailable.
if (-f $self->pid_file) {
error "Removing old PID file -- " .
"Unclean shutdown of previous test run?\n";
unlink $self->pid_file;
}
$old_pid = 0;
}
else {
$old_pid = $self->stop;
}
my $cmd = $self->start_cmd;
my $config = $self->{config};
my $vars = $config->{vars};
my $httpd = $vars->{httpd} || 'unknown';
if ($old_pid == -1) {
return 0;
}
local $| = 1;
unless (-x $httpd) {
my $why = -e $httpd ? "is not executable" : "does not exist";
error "cannot start server: httpd ($httpd) $why";
return 0;
}
print "$cmd\n";
my $old_sig;
if (WIN32) {
#make sure only 1 process is started for win32
#else Kill will only shutdown the parent
my $one_process = $self->version_of(\%one_process);
require Win32::Process;
my $obj;
# We need the "1" below to inherit the calling processes
# handles when running Apache::TestSmoke so as to properly
# dup STDOUT/STDERR
Win32::Process::Create($obj,
$httpd,
"$cmd $one_process",
1,
Win32::Process::NORMAL_PRIORITY_CLASS(),
'.');
unless ($obj) {
die "Could not start the server: " .
Win32::FormatMessage(Win32::GetLastError());
}
$config->{win32obj} = $obj;
}
else {
$old_sig = $SIG{CHLD};
if (USE_SIGCHLD) {
# XXX: try not to be POSIX dependent
require POSIX;
#XXX: this is not working well on solaris or hpux
$SIG{CHLD} = sub {
while ((my $child = waitpid(-1, POSIX::WNOHANG())) > 0) {
my $status = $? >> 8;
#error "got child exit $status";
if ($status) {
my $msg = "server has died with status $status";
$self->failed_msg("\n$msg");
Apache::TestRun->new(test_config => $config)->scan_core;
kill SIGTERM => $$;
}
}
};
}
defined(my $pid = fork) or die "Can't fork: $!";
unless ($pid) { # child
my $status = system "$cmd";
if ($status) {
$status = $? >> 8;
#error "httpd didn't start! $status";
}
CORE::exit $status;
}
}
while ($old_pid and $old_pid == $self->pid) {
warning "old pid file ($old_pid) still exists";
sleep 1;
}
my $version = $self->{version};
my $mpm = $config->{mpm} || "";
$mpm = "($mpm MPM)" if $mpm;
print "using $version $mpm\n";
my $timeout = $vars->{startup_timeout} ||
$ENV{APACHE_TEST_STARTUP_TIMEOUT} ||
60;
my $start_time = time;
my $preamble = "${CTRL_M}waiting $timeout seconds for server to start: ";
print $preamble unless COLOR;
while (1) {
my $delta = time - $start_time;
print COLOR
? ($preamble, sprintf "%02d:%02d", (gmtime $delta)[1,0])
: '.';
sleep 1;
if ($self->pid) {
print $preamble, "ok (waited $delta secs)\n";
last;
}
elsif ($delta > $timeout) {
my $suggestion = $timeout + 300;
print $preamble, "not ok\n";
error <<EOI;
giving up after $delta secs. If you think that your system
is slow or overloaded try again with a longer timeout value.
by setting the environment variable APACHE_TEST_STARTUP_TIMEOUT
to a high value (e.g. $suggestion) and repeat the last command.
EOI
last;
}
}
# now that the server has started don't abort the test run if it
# dies
$SIG{CHLD} = $old_sig || 'DEFAULT';
if (my $pid = $self->pid) {
print "server $self->{name} started\n";
my $vh = $config->{vhosts};
my $by_port = sub { $vh->{$a}->{port} <=> $vh->{$b}->{port} };
for my $module (sort $by_port keys %$vh) {
print "server $vh->{$module}->{name} listening ($module)\n",
}
if ($config->configure_proxy) {
print "tests will be proxied through $vars->{proxy}\n";
}
}
else {
$self->failed_msg("server failed to start!");
return 0;
}
return 1 if $self->wait_till_is_up($timeout);
$self->failed_msg("failed to start server!");
return 0;
}
# wait till the server is up and return 1
# if the waiting times out returns 0
sub wait_till_is_up {
my($self, $timeout) = @_;
my $config = $self->{config};
my $sleep_interval = 1; # secs
my $server_up = sub {
local $SIG{__WARN__} = sub {}; #avoid "cannot connect ..." warnings
# avoid fatal errors when LWP is not available
return eval {
my $r=Apache::TestRequest::GET('/index.html');
$r->code!=500 or $r->header('client-warning')!~/internal/i;
} || 0;
};
if ($server_up->()) {
return 1;
}
my $start_time = time;
my $preamble = "${CTRL_M}still waiting for server to warm up: ";
print $preamble unless COLOR;
while (1) {
my $delta = time - $start_time;
print COLOR
? ($preamble, sprintf "%02d:%02d", (gmtime $delta)[1,0])
: '.';
sleep $sleep_interval;
if ($server_up->()) {
print "${CTRL_M}the server is up (waited $delta secs) \n";
return 1;
}
elsif ($delta > $timeout) {
print "${CTRL_M}the server is down, giving up after $delta secs\n";
return 0;
}
else {
# continue
}
}
}
1;

View file

@ -0,0 +1,949 @@
# 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::TestSmoke;
use strict;
use warnings FATAL => 'all';
use Apache::Test ();
use Apache::TestConfig ();
use Apache::TestTrace;
use Apache::TestHarness ();
use Apache::TestRun (); # for core scan functions
use Apache::TestSort;
use Getopt::Long qw(GetOptions);
use File::Spec::Functions qw(catfile);
use FindBin;
use POSIX ();
use Symbol ();
#use constant DEBUG => 1;
# how many times to run all tests at the first iteration
use constant DEFAULT_TIMES => 10;
# if after this number of tries to reduce the number of tests fails we
# give up on more tries
use constant MAX_REDUCTION_TRIES => 50;
my @num_opts = qw(times);
my @string_opts = qw(order report);
my @flag_opts = qw(help verbose bug_mode);
my %order = map {$_ => 1} qw(random repeat);
my %usage = (
'times=N' => 'how many times to run the entire test suite' .
' (default: ' . DEFAULT_TIMES . ')',
'order=MODE' => 'modes: random, repeat' .
' (default: random)',
'report=FILENAME' => 'save report in a filename' .
' (default: smoke-report-<date>.txt)',
'verbose[=1]' => 'verbose output' .
' (default: 0)',
'bug_mode' => 'bug report mode' .
' (default: 0)',
);
sub new {
my($class, @argv) = @_;
my $self = bless {
seen => {}, # seen sequences and tried them md5 hash
results => {}, # final reduced sequences md5 hash
smoking_completed => 0,
tests => [],
total_iterations => 0,
total_reduction_attempts => 0,
total_reduction_successes => 0,
total_tests_run => 0,
}, ref($class)||$class;
$self->{test_config} = Apache::TestConfig->thaw;
$self->getopts(\@argv);
my $opts = $self->{opts};
chdir "$FindBin::Bin/..";
$self->{times} = $opts->{times} || DEFAULT_TIMES;
$self->{order} = $opts->{order} || 'random';
$self->{verbose} = $opts->{verbose} || 0;
$self->{run_iter} = $self->{times};
# this is like 'make test' but produces an output to be used in
# the bug report
if ($opts->{bug_mode}) {
$self->{bug_mode} = 1;
$self->{run_iter} = 1;
$self->{times} = 1;
$self->{verbose} = 1;
$self->{order} = 'random';
$self->{trace} = 'debug';
}
# specific tests end up in $self->{tests} and $self->{subtests};
# and get removed from $self->{argv}
$self->Apache::TestRun::split_test_args();
my $test_opts = {
verbose => $self->{verbose},
tests => $self->{tests},
order => $self->{order},
subtests => $self->{subtests} || [],
};
@{ $self->{tests} } = $self->get_tests($test_opts);
$self->{base_command} = "$^X $FindBin::Bin/TEST";
# options common to all
$self->{base_command} .= " -verbose" if $self->{verbose};
# options specific to the startup
$self->{start_command} = "$self->{base_command} -start";
$self->{start_command} .= " -trace=" . $self->{trace} if $self->{trace};
# options specific to the run
$self->{run_command} = "$self->{base_command} -run";
# options specific to the stop
$self->{stop_command} = "$self->{base_command} -stop";
$self;
}
sub getopts {
my($self, $argv) = @_;
my %opts;
local *ARGV = $argv;
# permute : optional values can come before the options
# pass_through : all unknown things are to be left in @ARGV
Getopt::Long::Configure(qw(pass_through permute));
# grab from @ARGV only the options that we expect
GetOptions(\%opts, @flag_opts,
(map "$_=s", @string_opts),
(map "$_=i", @num_opts));
if (exists $opts{order} && !exists $order{$opts{order}}) {
error "unknown -order mode: $opts{order}";
$self->opt_help();
exit;
}
if ($opts{help}) {
$self->opt_help;
exit;
}
# min
$self->{opts} = \%opts;
$self->{argv} = [@ARGV];
}
# XXX: need proper sub-classing
# from Apache::TestHarness
sub skip { Apache::TestHarness::skip(@_); }
sub prune { Apache::TestHarness::prune(@_); }
sub get_tests { Apache::TestHarness::get_tests(@_);}
sub install_sighandlers {
my $self = shift;
$SIG{INT} = sub {
# make sure that there the server is down
$self->kill_proc();
$self->report_finish;
exit;
};
}
END {
local $?; # preserve the exit status
eval {
Apache::TestRun->new(test_config =>
Apache::TestConfig->thaw)->scan_core;
};
}
sub run {
my($self) = shift;
$self->Apache::TestRun::warn_core();
local $SIG{INT};
$self->install_sighandlers;
$self->report_start();
if ($self->{bug_mode}) {
# 'make test', but useful for bug reports
$self->run_bug_mode();
}
else {
# normal smoke
my $iter = 0;
while ($iter++ < $self->{run_iter}) {
my $last = $self->run_iter($iter);
last if $last;
}
}
$self->{smoking_completed} = 1;
$self->report_finish();
exit;
}
sub sep {
my($char, $title) = @_;
my $width = 60;
if ($title) {
my $side = int( ($width - length($title) - 2) / 2);
my $pad = ($side+1) * 2 + length($title) < $width ? 1 : 0;
return $char x $side . " $title " . $char x ($side+$pad);
}
else {
return $char x $width;
}
}
my %log_files = ();
use constant FH => 0;
use constant POS => 1;
sub logs_init {
my($self, @log_files) = @_;
for my $path (@log_files) {
my $fh = Symbol::gensym();
open $fh, "<$path" or die "Can't open $path: $!";
seek $fh, 0, POSIX::SEEK_END();
$log_files{$path}[FH] = $fh;
$log_files{$path}[POS] = tell $fh;
}
}
sub logs_end {
for my $path (keys %log_files) {
close $log_files{$path}[FH];
}
}
sub log_diff {
my($self, $path) = @_;
my $log = $log_files{$path};
die "no such log file: $path" unless $log;
my $fh = $log->[FH];
# no checkpoints were made yet?
unless (defined $log->[POS]) {
seek $fh, 0, POSIX::SEEK_END();
$log->[POS] = tell $fh;
return '';
}
seek $fh, $log->[POS], POSIX::SEEK_SET(); # not really needed
local $/; # slurp mode
my $diff = <$fh>;
seek $fh, 0, POSIX::SEEK_END(); # not really needed
$log->[POS] = tell $fh;
return $diff || '';
}
# this is a special mode, which really just runs 't/TEST -start;
# t/TEST -run; t/TEST -stop;' but it runs '-run' separately for each
# test, and checks whether anything bad has happened after the run
# of each test (i.e. either a test has failed, or a test may be successful,
# but server may have dumped a core file, we detect that).
sub run_bug_mode {
my($self) = @_;
my $iter = 0;
warning "running t/TEST in the bug report mode";
my $reduce_iter = 0;
my @good = ();
# first time run all tests, or all specified tests
my @tests = @{ $self->{tests} }; # copy
my $bad = $self->run_test($iter, $reduce_iter, \@tests, \@good);
$self->{total_iterations}++;
}
# returns true if for some reason no more iterations should be made
sub run_iter {
my($self, $iter) = @_;
my $stop_now = 0;
my $reduce_iter = 0;
my @good = ();
warning "\n" . sep("-");
warning sprintf "[%03d-%02d-%02d] running all tests",
$iter, $reduce_iter, $self->{times};
# first time run all tests, or all specified tests
my @tests = @{ $self->{tests} }; # copy
# hack to ensure a new random seed is generated
Apache::TestSort->run(\@tests, $self);
my $bad = $self->run_test($iter, $reduce_iter, \@tests, \@good);
unless ($bad) {
$self->{total_iterations}++;
return $stop_now;
}
error "recorded a positive failure ('$bad'), " .
"will try to minimize the input now";
my $command = $self->{base_command};
# does the test fail on its own
{
$reduce_iter++;
warning sprintf "[%03d-%02d-%02d] trying '$bad' on its own",
$iter, $reduce_iter, 1;
my @good = ();
my @tests = ($bad);
my $bad = $self->run_test($iter, $reduce_iter, \@tests, \@good);
# if a test is failing on its own there is no point to
# continue looking for other sequences
if ($bad) {
$stop_now = 1;
$self->{total_iterations}++;
unless ($self->sequence_seen($self->{results}, [@good, $bad])) {
$self->report_success($iter, $reduce_iter, "$command $bad", 1);
}
return $stop_now;
}
}
# positive failure
my $ok_tests = @good;
my $reduction_success = 0;
my $done = 0;
while (@good > 1) {
my $tries = 0;
my $reduce_sub = $self->reduce_stream(\@good);
$reduce_iter++;
while ($tries++ < MAX_REDUCTION_TRIES) {
$self->{total_reduction_attempts}++;
my @try = @{ $reduce_sub->() };
# reduction stream is empty (tried all?)
unless (@try) {
$done = 1;
last;
}
warning sprintf "\n[%03d-%02d-%02d] trying %d tests",
$iter, $reduce_iter, $tries, scalar(@try);
my @ok = ();
my @tests = (@try, $bad);
my $new_bad = $self->run_test($iter, $reduce_iter, \@tests, \@ok);
if ($new_bad) {
# successful reduction
$reduction_success++;
@good = @ok;
$tries = 0;
my $num = @ok;
error "*** reduction $reduce_iter succeeded ($num tests) ***";
$self->{total_reduction_successes}++;
$self->log_successful_reduction($iter, \@ok);
last;
}
}
# last round of reducing has failed, so we give up
if ($done || $tries >= MAX_REDUCTION_TRIES){
error "no further reductions were made";
$done = 1;
last;
}
}
# we have a minimal failure sequence at this point (to the extend
# of success of our attempts to reduce)
# report the sequence if we didn't see such one yet in the
# previous iterations
unless ($self->sequence_seen($self->{results}, [@good, $bad])) {
# if no reduction succeeded, it's 0
$reduce_iter = 0 unless $reduction_success;
$self->report_success($iter, $reduce_iter,
"$command @good $bad", @good + 1);
}
$self->{total_iterations}++;
return $stop_now;
}
# my $sub = $self->reduce_stream(\@items);
sub reduce_stream {
my($self) = shift;
my @items = @{+shift};
my $items = @items;
my $odd = $items % 2 ? 1 : 0;
my $middle = int($items/2) - 1;
my $c = 0;
return sub {
$c++; # remember stream's state
# a single item is not reduce-able
return \@items if $items == 1;
my @try = ();
my $max_repeat_tries = 50; # avoid seen sequences
my $repeat = 0;
while ($repeat++ <= $max_repeat_tries) {
# try to use a binary search
if ($c == 1) {
# right half
@try = @items[($middle+1)..($items-1)];
}
elsif ($c == 2) {
# left half
@try = @items[0..$middle];
}
# try to use a random window size alg
else {
my $left = int rand($items);
$left = $items - 1 if $left == $items - 1;
my $right = $left + int rand($items - $left);
$right = $items - 1 if $right >= $items;
@try = @items[$left..$right];
}
if ($self->sequence_seen($self->{seen}, \@try)) {
@try = ();
}
else {
last; # found an unseen sequence
}
}
return \@try;
}
}
sub sequence_seen {
my ($self, $rh_store, $ra_tests) = @_;
require Digest::MD5;
my $digest = Digest::MD5::md5_hex(join '', @$ra_tests);
#error $self->{seen};
return $rh_store->{$digest}++ ? 1 : 0
}
sub run_test {
require IPC::Run3;
my($self, $iter, $count, $tests, $ra_ok) = @_;
my $bad = '';
my $ra_nok = [];
#warning "$self->{base_command} @$tests";
#$SIG{PIPE} = 'IGNORE';
$SIG{PIPE} = sub { die "pipe broke" };
# start server
{
my $command = $self->{start_command};
my $log = '';
IPC::Run3::run3($command, undef, \$log, \$log);
my $started_ok = ($log =~ /started/) ? 1 : 0;
unless ($started_ok) {
error "failed to start server\n $log";
exit 1;
}
}
my $t_logs = $self->{test_config}->{vars}->{t_logs};
my @log_files = map { catfile $t_logs, $_ } qw(error_log access_log);
$self->logs_init(@log_files);
# run tests
{
my $command = $self->{run_command};
my $max_len = 1;
for my $test (@$tests) {
$max_len = length $test if length $test > $max_len;
}
for my $test (@$tests) {
(my $test_name = $test) =~ s/\.t$//;
my $fill = "." x ($max_len - length $test_name);
$self->{total_tests_run}++;
my $test_command = "$command $test";
my $log = '';
IPC::Run3::run3($test_command, undef, \$log, \$log);
my $ok = ($log =~ /All tests successful|NOTESTS/) ? 1 : 0;
my @core_files_msg = $self->Apache::TestRun::scan_core_incremental(1);
# if the test has caused core file(s) it's not ok
$ok = 0 if @core_files_msg;
if ($ok == 1) {
push @$ra_ok, $test;
if ($self->{verbose}) {
if ($log =~ m/NOTESTS/) {
print STDERR "$test_name${fill}skipped\n";
} else {
print STDERR "$test_name${fill}ok\n";
}
}
# need to run log_diff to reset the position of the fh
my %log_diffs = map { $_ => $self->log_diff($_) } @log_files;
}
elsif ($ok == 0) {
push @$ra_nok, $test;
$bad = $test;
if ($self->{verbose}) {
print STDERR "$test_name${fill}FAILED\n";
error sep("-");
# give server some time to finish the
# logging. it's ok to wait long time since we have
# to deal with an error
sleep 5;
my %log_diffs = map { $_ => $self->log_diff($_) } @log_files;
# client log
error "\t\t*** run log ***";
$log =~ s/^/ /mg;
print STDERR "$log\n";
# server logs
for my $path (@log_files) {
next unless length $log_diffs{$path};
error "\t\t*** $path ***";
$log_diffs{$path} =~ s/^/ /mg;
print STDERR "$log_diffs{$path}\n";
}
}
if (@core_files_msg) {
unless ($self->{verbose}) {
# currently the output of 'run log' already
# includes the information about core files once
# Test::Harness::Straps allows us to run callbacks
# after each test, and we move back to run all
# tests at once, we will log the message here
error "$test_name caused core";
print STDERR join "\n", @core_files_msg, "\n";
}
}
if ($self->{verbose}) {
error sep("-");
}
unless ($self->{bug_mode}) {
# normal smoke stop the run, but in the bug_mode
# we want to complete all the tests
last;
}
}
}
}
$self->logs_end();
# stop server
$self->kill_proc();
if ($self->{bug_mode}) {
warning sep("-");
if (@$ra_nok == 0) {
printf STDERR "All tests successful (%d)\n", scalar @$ra_ok;
}
else {
error sprintf "error running %d tests out of %d\n",
scalar(@$ra_nok), scalar @$ra_ok + @$ra_nok;
}
}
else {
return $bad;
}
}
sub report_start {
my($self) = shift;
my $time = scalar localtime;
$self->{start_time} = $time;
$time =~ s/\s/_/g;
$time =~ s/:/-/g; # winFU
my $file = $self->{opts}->{report} ||
catfile Apache::Test::vars('top_dir'), "smoke-report-$time.txt";
$self->{runtime}->{report} = $file;
info "Report file: $file";
open my $fh, ">$file" or die "cannot open $file for writing: $!";
$self->{fh} = $fh;
my $sep = sep("-");
my $title = sep('=', "Special Tests Sequence Failure Finder Report");
print $fh <<EOM;
$title
$sep
First iteration used:
$self->{base_command} @{$self->{tests}}
$sep
EOM
}
sub report_success {
my($self, $iter, $reduce_iter, $sequence, $tests) = @_;
my @report = ("iteration $iter ($tests tests):\n",
"\t$sequence\n",
"(made $reduce_iter successful reductions)\n\n");
print @report;
if (my $fh = $self->{fh}) {
print $fh @report;
}
}
sub report_finish {
my($self) = @_;
my $start_time = $self->{start_time};
my $end_time = scalar localtime;
if (my $fh = delete $self->{fh}) {
my $failures = scalar keys %{ $self->{results} };
my $sep = sep("-");
my $cfg_as_string = $self->build_config_as_string;
my $unique_seqs = scalar keys %{ $self->{results} };
my $attempts = $self->{total_reduction_attempts};
my $successes = $self->{total_reduction_successes};
my $completion = $self->{smoking_completed}
? "Completed"
: "Not Completed (aborted by user)";
my $status = "Unknown";
if ($self->{total_iterations} > 0) {
if ($failures) {
$status = "*** NOT OK ***";
}
else {
$status = "+++ OK +++";
}
}
my $title = sep('=', "Summary");
my $iter_made = sprintf "Iterations (%s) made : %d",
$self->{order}, $self->{total_iterations};
print $fh <<EOM;
$title
Completion : $completion
Status : $status
Tests run : $self->{total_tests_run}
$iter_made
EOM
if ($attempts > 0 && $failures) {
my $reduction_stats = sprintf "%d/%d (%d%% success)",
$attempts, $successes, $successes / $attempts * 100;
print $fh <<EOM;
Unique sequences found : $unique_seqs
Reduction tries/success : $reduction_stats
EOM
}
print $fh <<EOM;
$sep
--- Started at: $start_time ---
--- Ended at: $end_time ---
$sep
The smoke testing was run on the system with the following
parameters:
$cfg_as_string
-- this report was generated by $0
EOM
close $fh;
}
}
# in case the smoke gets killed before it had a chance to finish and
# write the report, at least we won't lose the last successful reduction
# XXX: this wasn't needed before we switched to IPC::Run3, since
# Ctrl-C would log the collected data, but it doesn't work with
# IPC::Run3. So if that gets fixed, we can remove that function
sub log_successful_reduction {
my($self, $iter, $tests) = @_;
my $file = $self->{runtime}->{report} . ".$iter.temp";
debug "saving in $file";
open my $fh, ">$file" or die "cannot open $file for writing: $!";
print $fh join " ", @$tests;
close $fh;
}
sub build_config_as_string {
Apache::TestConfig::as_string();
}
sub kill_proc {
my($self) = @_;
my $command = $self->{stop_command};
my $log = '';
require IPC::Run3;
IPC::Run3::run3($command, undef, \$log, \$log);
my $stopped_ok = ($log =~ /shutdown/) ? 1 : 0;
unless ($stopped_ok) {
error "failed to stop server\n $log";
}
}
sub opt_help {
my $self = shift;
print <<EOM;
usage: t/SMOKE [options ...] [tests]
where the options are:
EOM
for (sort keys %usage){
printf " -%-16s %s\n", $_, $usage{$_};
}
print <<EOM;
if 'tests' argument is not provided all available tests will be run
EOM
}
# generate t/SMOKE script (or a different filename) which will drive
# Apache::TestSmoke
sub generate_script {
my ($class, $file) = @_;
$file ||= catfile 't', 'SMOKE';
my $content = join "\n",
"BEGIN { eval { require blib && blib->import; } }",
Apache::TestConfig->perlscript_header,
"use $class;",
"$class->new(\@ARGV)->run;";
Apache::Test::basic_config()->write_perlscript($file, $content);
}
1;
__END__
=head1 NAME
Apache::TestSmoke - Special Tests Sequence Failure Finder
=head1 SYNOPSIS
# get the usage and the default values
% t/SMOKE -help
# repeat all tests 5 times and save the report into
# the file 'myreport'
% t/SMOKE -times=5 -report=myreport
# run all tests default number of iterations, and repeat tests
# default number of times
% t/SMOKE
# same as above but work only the specified tests
% t/SMOKE foo/bar foo/tar
# run once a sequence of tests in a non-random mode
# e.g. when trying to reduce a known long sequence that fails
% t/SMOKE -order=rotate -times=1 foo/bar foo/tar
# show me each currently running test
# it's not the same as running the tests in the verbose mode
% t/SMOKE -verbose
# run t/TEST, but show any problems after *each* tests is run
# useful for bug reports (it actually runs t/TEST -start, then
# t/TEST -run for each test separately and finally t/TEST -stop
% t/SMOKE -bug_mode
# now read the created report file
=head1 DESCRIPTION
=head2 The Problem
When we try to test a stateless machine (i.e. all tests are
independent), running all tests once ensures that all tested things
properly work. However when a state machine is tested (i.e. where a
run of one test may influence another test) it's not enough to run all
the tests once to know that the tested features actually work. It's
quite possible that if the same tests are run in a different order
and/or repeated a few times, some tests may fail. This usually
happens when some tests don't restore the system under test to its
pristine state at the end of the run, which may influence other tests
which rely on the fact that they start on pristine state, when in fact
it's not true anymore. In fact it's possible that a single test may
fail when run twice or three times in a sequence.
=head2 The Solution
To reduce the possibility of such dependency errors, it's helpful to
run random testing repeated many times with many different srand
seeds. Of course if no failures get spotted that doesn't mean that
there are no tests inter-dependencies, which may cause a failure in
production. But random testing definitely helps to spot many problems
and can give better test coverage.
=head2 Resolving Sequence Problems
When this kind of testing is used and a failure is detected there are
two problems:
=over
=item 1
First is to be able to reproduce the problem so if we think we fixed
it, we could verify the fix. This one is easy, just remember the
sequence of tests run till the failed test and rerun the same sequence
once again after the problem has been fixed.
=item 2
Second is to be able to understand the cause of the problem. If during
the random test the failure has happened after running 400 tests, how
can we possibly know which previously running tests has caused to the
failure of the test 401. Chances are that most of the tests were clean
and don't have inter-dependency problem. Therefore it'd be very
helpful if we could reduce the long sequence to a minimum. Preferably
1 or 2 tests. That's when we can try to understand the cause of the
detected problem.
=back
This utility attempts to solve both problems, and at the end of each
iteration print a minimal sequence of tests causing to a failure. This
doesn't always succeed, but works in many cases.
This utility:
=over
=item 1
Runs the tests randomly until the first failure is detected. Or
non-randomly if the option I<-order> is set to I<repeat> or I<rotate>.
=item 2
Then it tries to reduce that sequence of tests to a minimum, and this
sequence still causes to the same failure.
=item 3
(XXX: todo): then it reruns the minimal sequence in the verbose mode
and saves the output.
=item 4
It reports all the successful reductions as it goes to STDOUT and
report file of the format: smoke-report-<date>.txt.
In addition the systems build parameters are logged into the report
file, so the detected problems could be reproduced.
=item 5
Goto 1 and run again using a new random seed, which potentially should
detect different failures.
=back
=head1 Reduction Algorithm
Currently for each reduction path, the following reduction algorithms
get applied:
=over
=item 1
Binary search: first try the upper half then the lower.
=item 2
Random window: randomize the left item, then the right item and return
the items between these two points.
=back
=head1 t/SMOKE.PL
I<t/SMOKE.PL> is driving this module, if you don't have it, create it:
#!perl
use strict;
use warnings FATAL => 'all';
use FindBin;
use lib "$FindBin::Bin/../Apache-Test/lib";
use lib "$FindBin::Bin/../lib";
use Apache::TestSmoke ();
Apache::TestSmoke->new(@ARGV)->run;
usually I<Makefile.PL> converts it into I<t/SMOKE> while adjusting the
perl path, but you create I<t/SMOKE> in first place as well.
=head1 AUTHOR
Stas Bekman
=cut

View file

@ -0,0 +1,34 @@
# 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::TestSmokePerl;
use strict;
use warnings FATAL => 'all';
use Apache::TestSmoke ();
use ModPerl::Config ();
# a subclass of Apache::TestSmoke that configures mod_perlish things
use vars qw(@ISA);
@ISA = qw(Apache::TestSmoke);
sub build_config_as_string {
ModPerl::Config::as_string();
}
1;
__END__

View file

@ -0,0 +1,76 @@
# 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::TestSort;
use strict;
use warnings FATAL => 'all';
use Apache::TestTrace;
sub repeat {
my($list) = @_;
return @{$list};
}
sub random {
my($list) = @_;
my $seed = $ENV{APACHE_TEST_SEED} || '';
my $info = "";
if ($seed) {
$info = " (user defined)";
# so we could reproduce the order
}
else {
$info = " (autogenerated)";
$seed = time ^ ($$ + ($$ << 15));
}
warning "Using random number seed: $seed" . $info;
srand($seed);
#from perlfaq4.pod
for (my $i = @$list; --$i; ) {
my $j = int rand($i+1);
next if $i == $j;
@$list[$i,$j] = @$list[$j,$i];
}
}
sub run {
my($self, $list, $args) = @_;
my $order = $args->{order} || 'repeat';
if ($order =~ /^\d+$/) {
#dont want an explicit -seed option but env var can be a pain
#so if -order is number assume it is the random seed
$ENV{APACHE_TEST_SEED} = $order;
$order = 'random';
}
my $sort = \&{$order};
# re-shuffle the list according to the requested order
if (defined &$sort) {
$sort->($list);
}
else {
error "unknown order '$order'";
}
}
1;

View file

@ -0,0 +1,256 @@
# 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::TestTrace;
use strict;
use warnings FATAL => 'all';
use Exporter ();
use vars qw(@Levels @Utils @Level_subs @Util_subs
@ISA @EXPORT $VERSION $Level $LogFH);
BEGIN {
@Levels = qw(emerg alert crit error warning notice info debug);
@Utils = qw(todo);
@Level_subs = map {($_, "${_}_mark", "${_}_sub")} (@Levels);
@Util_subs = map {($_, "${_}_mark", "${_}_sub")} (@Utils);
}
@ISA = qw(Exporter);
@EXPORT = (@Level_subs);
$VERSION = '0.01';
use subs (@Level_subs, @Util_subs);
# default settings overrideable by users
$Level = undef;
$LogFH = \*STDERR;
# private data
use constant COLOR => ($ENV{APACHE_TEST_COLOR} && -t STDOUT) ? 1 : 0;
use constant HAS_COLOR => eval {
#XXX: another way to color WINFU terms?
!(grep { $^O eq $_ } qw(MSWin32 cygwin NetWare)) and
COLOR and require Term::ANSIColor;
};
use constant HAS_DUMPER => eval { require Data::Dumper; };
# emerg => 1, alert => 2, crit => 3, ...
my %levels; @levels{@Levels} = 1..@Levels;
$levels{todo} = $levels{debug};
my $default_level = 'info'; # to prevent user typos
my %colors = ();
if (HAS_COLOR) {
%colors = (
emerg => 'bold white on_blue',
alert => 'bold blue on_yellow',
crit => 'reverse',
error => 'bold red',
warning => 'yellow',
notice => 'green',
info => 'cyan',
debug => 'magenta',
reset => 'reset',
todo => 'underline',
);
$Term::ANSIColor::AUTORESET = 1;
for (keys %colors) {
$colors{$_} = Term::ANSIColor::color($colors{$_});
}
}
*expand = HAS_DUMPER ?
sub { map { ref $_ ? Data::Dumper::Dumper($_) : $_ } @_ } :
sub { @_ };
sub prefix {
my $prefix = shift;
if ($prefix eq 'mark') {
return join(":", (caller(3))[1..2]) . " : ";
}
elsif ($prefix eq 'sub') {
return (caller(3))[3] . " : ";
}
else {
return '';
}
}
sub c_trace {
my ($level, $prefix_type) = (shift, shift);
my $prefix = prefix($prefix_type);
print $LogFH
map { "$colors{$level}$prefix$_$colors{reset}\n"}
grep defined($_), expand(@_);
}
sub nc_trace {
my ($level, $prefix_type) = (shift, shift);
my $prefix = prefix($prefix_type);
print $LogFH
map { sprintf "[%7s] %s%s\n", $level, $prefix, $_ }
grep defined($_), expand(@_);
}
{
my $trace = HAS_COLOR ? \&c_trace : \&nc_trace;
my @prefices = ('', 'mark', 'sub');
# if the level is sufficiently high, enable the tracing for a
# given level otherwise assign NOP
for my $level (@Levels, @Utils) {
no strict 'refs';
for my $prefix (@prefices) {
my $func = $prefix ? "${level}_$prefix" : $level;
*$func = sub { $trace->($level, $prefix, @_)
if trace_level() >= $levels{$level};
};
}
}
}
sub trace_level {
# overriden by user/-trace
(defined $Level && $levels{$Level}) ||
# or overriden by env var
(exists $ENV{APACHE_TEST_TRACE_LEVEL} &&
$levels{$ENV{APACHE_TEST_TRACE_LEVEL}}) ||
# or default
$levels{$default_level};
}
1;
__END__
=head1 NAME
Apache::TestTrace - Helper output generation functions
=head1 SYNOPSIS
use Apache::TestTrace;
debug "foo bar";
info_sub "missed it";
error_mark "something is wrong";
# test sub that exercises all the tracing functions
sub test {
print $Apache::TestTrace::LogFH
"TraceLevel: $Apache::TestTrace::Level\n";
$_->($_,[1..3],$_) for qw(emerg alert crit error
warning notice info debug todo);
print $Apache::TestTrace::LogFH "\n\n"
};
# demo the trace subs using default setting
test();
{
# override the default trace level with 'crit'
local $Apache::TestTrace::Level = 'crit';
# now only 'crit' and higher levels will do tracing lower level
test();
}
{
# set the trace level to 'debug'
local $Apache::TestTrace::Level = 'debug';
# now only 'debug' and higher levels will do tracing lower level
test();
}
{
open OUT, ">/tmp/foo" or die $!;
# override the default Log filehandle
local $Apache::TestTrace::LogFH = \*OUT;
# now the traces will go into a new filehandle
test();
close OUT;
}
# override tracing level via -trace opt
% t/TEST -trace=debug
# override tracing level via env var
% env APACHE_TEST_TRACE_LEVEL=debug t/TEST
=head1 DESCRIPTION
This module exports a number of functions that make it easier
generating various diagnostics messages in your programs in a
consistent way and saves some keystrokes as it handles the new lines
and sends the messages to STDERR for you.
This module provides the same trace methods as syslog(3)'s log
levels. Listed from low level to high level: emerg(), alert(), crit(),
error(), warning(), notice(), info(), debug(). The only different
function is warning(), since warn is already taken by Perl.
The module provides another trace function called todo() which is
useful for todo items. It has the same level as I<debug> (the
highest).
There are two more variants of each of these functions. If the
I<_mark> suffix is appended (e.g., I<error_mark>) the trace will start
with the filename and the line number the function was called from. If
the I<_sub> suffix is appended (e.g., I<error_info>) the trace will
start with the name of the subroutine the function was called from.
If you have C<Term::ANSIColor> installed the diagnostic messages will
be colorized, otherwise a special for each function prefix will be
used.
If C<Data::Dumper> is installed and you pass a reference to a variable
to any of these functions, the variable will be dumped with
C<Data::Dumper::Dumper()>.
Functions whose level is above the level set in
C<$Apache::TestTrace::Level> become NOPs. For example if the level is
set to I<alert>, only alert() and emerg() functions will generate the
output. The default setting of this variable is I<warning>. Other
valid values are: I<emerg>, I<alert>, I<crit>, I<error>, I<warning>,
I<notice>, I<info>, I<debug>.
Another way to affect the trace level is to set
C<$ENV{APACHE_TEST_TRACE_LEVEL}>, which takes effect if
C<$Apache::TestTrace::Level> is not set. So an explicit setting of
C<$Apache::TestTrace::Level> always takes precedence.
By default all the output generated by these functions goes to
STDERR. You can override the default filehandler by overriding
C<$Apache::TestTrace::LogFH> with a new filehandler.
When you override this package's global variables, think about
localizing your local settings, so it won't affect other modules using
this module in the same run.
=head1 TODO
o provide an option to disable the coloring altogether via some flag
or import()
=head1 AUTHOR
Stas Bekman with contributions from Doug MacEachern
=cut

View file

@ -0,0 +1,989 @@
# 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::TestUtil;
use strict;
use warnings FATAL => 'all';
use File::Find ();
use File::Path ();
use Exporter ();
use Carp ();
use Config;
use File::Basename qw(dirname);
use File::Spec::Functions qw(catfile catdir file_name_is_absolute tmpdir);
use Symbol ();
use Fcntl qw(SEEK_END);
use Apache::Test ();
use Apache::TestConfig ();
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %CLEAN);
$VERSION = '0.02';
@ISA = qw(Exporter);
@EXPORT = qw(t_cmp t_debug t_append_file t_write_file t_open_file
t_mkdir t_rmtree t_is_equal t_filepath_cmp t_write_test_lib
t_server_log_error_is_expected t_server_log_warn_is_expected
t_client_log_error_is_expected t_client_log_warn_is_expected
);
@EXPORT_OK = qw(t_write_perl_script t_write_shell_script t_chown
t_catfile_apache t_catfile t_file_watch_for
t_start_error_log_watch t_finish_error_log_watch
t_start_file_watch t_read_file_watch t_finish_file_watch);
%CLEAN = ();
$Apache::TestUtil::DEBUG_OUTPUT = \*STDOUT;
# 5.005's Data::Dumper has problems to dump certain datastructures
use constant HAS_DUMPER => eval { $] >= 5.006 && require Data::Dumper; };
use constant INDENT => 4;
{
my %files;
sub t_start_file_watch (;$) {
my $name = defined $_[0] ? $_[0] : 'error_log';
$name = File::Spec->catfile(Apache::Test::vars->{t_logs}, $name)
unless (File::Spec->file_name_is_absolute($name));
if (open my $fh, '<', $name) {
seek $fh, 0, SEEK_END;
$files{$name} = $fh;
}
else {
delete $files{$name};
}
return;
}
sub t_finish_file_watch (;$) {
my $name = defined $_[0] ? $_[0] : 'error_log';
$name = File::Spec->catfile(Apache::Test::vars->{t_logs}, $name)
unless (File::Spec->file_name_is_absolute($name));
my $fh = delete $files{$name};
unless (defined $fh) {
open $fh, '<', $name or return;
return readline $fh;
}
return readline $fh;
}
sub t_read_file_watch (;$) {
my $name = defined $_[0] ? $_[0] : 'error_log';
$name = File::Spec->catfile(Apache::Test::vars->{t_logs}, $name)
unless (File::Spec->file_name_is_absolute($name));
my $fh = $files{$name};
unless (defined $fh) {
open $fh, '<', $name or return;
$files{$name} = $fh;
}
return readline $fh;
}
sub t_file_watch_for ($$$) {
my ($name, $re, $timeout) = @_;
local $/ = "\n";
$re = qr/$re/ unless ref $re;
$timeout *= 10;
my $buf = '';
my @acc;
while ($timeout >= 0) {
my $line = t_read_file_watch $name;
unless (defined $line) { # EOF
select undef, undef, undef, 0.1;
$timeout--;
next;
}
$buf .= $line;
next unless $buf =~ /\n$/; # incomplete line
# found a complete line
$line = $buf;
$buf = '';
push @acc, $line;
return wantarray ? @acc : $line if $line =~ $re;
}
return;
}
sub t_start_error_log_watch {
t_start_file_watch;
}
sub t_finish_error_log_watch {
local $/ = "\n";
return my @lines = t_finish_file_watch;
}
}
# because of the prototype and recursive call to itself a forward
# declaration is needed
sub t_is_equal ($$);
# compare any two datastructures (must pass references for non-scalars)
# undef()'s are valid args
sub t_is_equal ($$) {
my ($a, $b) = @_;
return 0 unless @_ == 2;
# this was added in Apache::Test::VERSION 1.12 - remove deprecated
# logic sometime around 1.15 or mid September, 2004.
if (UNIVERSAL::isa($a, 'Regexp')) {
my @warning = ("WARNING!!! t_is_equal() argument order has changed.",
"use of a regular expression as the first argument",
"is deprecated. support will be removed soon.");
t_debug(@warning);
($a, $b) = ($b, $a);
}
if (defined $a && defined $b) {
my $ref_a = ref $a;
my $ref_b = ref $b;
if (!$ref_a && !$ref_b) {
return $a eq $b;
}
elsif ($ref_a eq 'ARRAY' && $ref_b eq 'ARRAY') {
return 0 unless @$a == @$b;
for my $i (0..$#$a) {
t_is_equal($a->[$i], $b->[$i]) || return 0;
}
}
elsif ($ref_a eq 'HASH' && $ref_b eq 'HASH') {
return 0 unless (keys %$a) == (keys %$b);
for my $key (sort keys %$a) {
return 0 unless exists $b->{$key};
t_is_equal($a->{$key}, $b->{$key}) || return 0;
}
}
elsif ($ref_b eq 'Regexp') {
return $a =~ $b;
}
else {
# try to compare the references
return $a eq $b;
}
}
else {
# undef == undef! a valid test
return (defined $a || defined $b) ? 0 : 1;
}
return 1;
}
sub t_cmp ($$;$) {
Carp::carp(join(":", (caller)[1..2]) .
' usage: $res = t_cmp($received, $expected, [$comment])')
if @_ < 2 || @_ > 3;
my ($received, $expected) = @_;
# this was added in Apache::Test::VERSION 1.12 - remove deprecated
# logic sometime around 1.15 or mid September, 2004.
if (UNIVERSAL::isa($_[0], 'Regexp')) {
my @warning = ("WARNING!!! t_cmp() argument order has changed.",
"use of a regular expression as the first argument",
"is deprecated. support will be removed soon.");
t_debug(@warning);
($received, $expected) = ($expected, $received);
}
t_debug("testing : " . pop) if @_ == 3;
t_debug("expected: " . struct_as_string(0, $expected));
t_debug("received: " . struct_as_string(0, $received));
return t_is_equal($received, $expected);
}
# Essentially t_cmp, but on Win32, first converts pathnames
# to their DOS long name.
sub t_filepath_cmp ($$;$) {
my @a = (shift, shift);
if (Apache::TestConfig::WIN32) {
$a[0] = Win32::GetLongPathName($a[0]) if defined $a[0] && -e $a[0];
$a[1] = Win32::GetLongPathName($a[1]) if defined $a[1] && -e $a[1];
}
return @_ == 1 ? t_cmp($a[0], $a[1], $_[0]) : t_cmp($a[0], $a[1]);
}
*expand = HAS_DUMPER ?
sub { map { ref $_ ? Data::Dumper::Dumper($_) : $_ } @_ } :
sub { @_ };
sub t_debug {
my $out = $Apache::TestUtil::DEBUG_OUTPUT;
print $out map {"# $_\n"} map {split /\n/} grep {defined} expand(@_);
}
sub t_open_file {
my $file = shift;
die "must pass a filename" unless defined $file;
# create the parent dir if it doesn't exist yet
makepath(dirname $file);
my $fh = Symbol::gensym();
open $fh, ">$file" or die "can't open $file: $!";
t_debug("writing file: $file");
$CLEAN{files}{$file}++;
return $fh;
}
sub _temp_package_dir {
return catdir(tmpdir(), 'apache_test');
}
sub t_write_test_lib {
my $file = shift;
die "must pass a filename" unless defined $file;
t_write_file(catdir(_temp_package_dir(), $file), @_);
}
sub t_write_file {
my $file = shift;
die "must pass a filename" unless defined $file;
# create the parent dir if it doesn't exist yet
makepath(dirname $file);
my $fh = Symbol::gensym();
open $fh, ">$file" or die "can't open $file: $!";
t_debug("writing file: $file");
print $fh join '', @_ if @_;
close $fh;
$CLEAN{files}{$file}++;
}
sub t_append_file {
my $file = shift;
die "must pass a filename" unless defined $file;
# create the parent dir if it doesn't exist yet
makepath(dirname $file);
# add to the cleanup list only if we created it now
$CLEAN{files}{$file}++ unless -e $file;
my $fh = Symbol::gensym();
open $fh, ">>$file" or die "can't open $file: $!";
print $fh join '', @_ if @_;
close $fh;
}
sub t_write_shell_script {
my $file = shift;
my $code = join '', @_;
my($ext, $shebang);
if (Apache::TestConfig::WIN32()) {
$code =~ s/echo$/echo./mg; #required to echo newline
$ext = 'bat';
$shebang = "\@echo off\nREM this is a bat";
}
else {
$ext = 'sh';
$shebang = '#!/bin/sh';
}
$file .= ".$ext";
t_write_file($file, "$shebang\n", $code);
$ext;
}
sub t_write_perl_script {
my $file = shift;
my $shebang = "#!$Config{perlpath}\n";
my $warning = Apache::TestConfig->thaw->genwarning($file);
t_write_file($file, $shebang, $warning, @_);
chmod 0755, $file;
}
sub t_mkdir {
my $dir = shift;
makepath($dir);
}
# returns a list of dirs successfully created
sub makepath {
my($path) = @_;
return if !defined($path) || -e $path;
my $full_path = $path;
# remember which dirs were created and should be cleaned up
while (1) {
$CLEAN{dirs}{$path} = 1;
$path = dirname $path;
last if -e $path;
}
return File::Path::mkpath($full_path, 0, 0755);
}
sub t_rmtree {
die "must pass a dirname" unless defined $_[0];
File::Path::rmtree((@_ > 1 ? \@_ : $_[0]), 0, 1);
}
#chown a file or directory to the test User/Group
#noop if chown is unsupported
sub t_chown {
my $file = shift;
my $config = Apache::Test::config();
my($uid, $gid);
eval {
#XXX cache this lookup
($uid, $gid) = (getpwnam($config->{vars}->{user}))[2,3];
};
if ($@) {
if ($@ =~ /^The getpwnam function is unimplemented/) {
#ok if unsupported, e.g. win32
return 1;
}
else {
die $@;
}
}
CORE::chown($uid, $gid, $file) || die "chown $file: $!";
}
# $string = struct_as_string($indent_level, $var);
#
# return any nested datastructure via Data::Dumper or ala Data::Dumper
# as a string. undef() is a valid arg.
#
# $indent_level should be 0 (used for nice indentation during
# recursive datastructure traversal)
sub struct_as_string{
return "???" unless @_ == 2;
my $level = shift;
return "undef" unless defined $_[0];
my $pad = ' ' x (($level + 1) * INDENT);
my $spad = ' ' x ($level * INDENT);
if (HAS_DUMPER) {
local $Data::Dumper::Terse = 1;
$Data::Dumper::Terse = $Data::Dumper::Terse; # warn
my $data = Data::Dumper::Dumper(@_);
$data =~ s/\n$//; # \n is handled by the caller
return $data;
}
else {
if (ref($_[0]) eq 'ARRAY') {
my @data = ();
for my $i (0..$#{ $_[0] }) {
push @data,
struct_as_string($level+1, $_[0]->[$i]);
}
return join "\n", "[", map({"$pad$_,"} @data), "$spad\]";
} elsif ( ref($_[0])eq 'HASH') {
my @data = ();
for my $key (keys %{ $_[0] }) {
push @data,
"$key => " .
struct_as_string($level+1, $_[0]->{$key});
}
return join "\n", "{", map({"$pad$_,"} @data), "$spad\}";
} else {
return $_[0];
}
}
}
my $banner_format =
"\n*** The following %s expected and harmless ***\n";
sub is_expected_banner {
my $type = shift;
my $count = @_ ? shift : 1;
sprintf $banner_format, $count == 1
? "$type entry is"
: "$count $type entries are";
}
sub t_server_log_is_expected {
print STDERR is_expected_banner(@_);
}
sub t_client_log_is_expected {
my $vars = Apache::Test::config()->{vars};
my $log_file = catfile $vars->{serverroot}, "logs", "error_log";
my $fh = Symbol::gensym();
open $fh, ">>$log_file" or die "Can't open $log_file: $!";
my $oldfh = select($fh); $| = 1; select($oldfh);
print $fh is_expected_banner(@_);
close $fh;
}
sub t_server_log_error_is_expected { t_server_log_is_expected("error", @_);}
sub t_server_log_warn_is_expected { t_server_log_is_expected("warn", @_); }
sub t_client_log_error_is_expected { t_client_log_is_expected("error", @_);}
sub t_client_log_warn_is_expected { t_client_log_is_expected("warn", @_); }
END {
# remove files that were created via this package
for (grep {-e $_ && -f _ } keys %{ $CLEAN{files} } ) {
t_debug("removing file: $_");
unlink $_;
}
# remove dirs that were created via this package
for (grep {-e $_ && -d _ } keys %{ $CLEAN{dirs} } ) {
t_debug("removing dir tree: $_");
t_rmtree($_);
}
}
# essentially File::Spec->catfile, but on Win32
# returns the long path name, if the file is absolute
sub t_catfile {
my $f = catfile(@_);
return $f unless file_name_is_absolute($f);
return Apache::TestConfig::WIN32 && -e $f ?
Win32::GetLongPathName($f) : $f;
}
# Apache uses a Unix-style specification for files, with
# forward slashes for directory separators. This is
# essentially File::Spec::Unix->catfile, but on Win32
# returns the long path name, if the file is absolute
sub t_catfile_apache {
my $f = File::Spec::Unix->catfile(@_);
return $f unless file_name_is_absolute($f);
return Apache::TestConfig::WIN32 && -e $f ?
Win32::GetLongPathName($f) : $f;
}
1;
__END__
=encoding utf8
=head1 NAME
Apache::TestUtil - Utility functions for writing tests
=head1 SYNOPSIS
use Apache::Test;
use Apache::TestUtil;
ok t_cmp("foo", "foo", "sanity check");
t_write_file("filename", @content);
my $fh = t_open_file($filename);
t_mkdir("/foo/bar");
t_rmtree("/foo/bar");
t_is_equal($a, $b);
=head1 DESCRIPTION
C<Apache::TestUtil> automatically exports a number of functions useful
in writing tests.
All the files and directories created using the functions from this
package will be automatically destroyed at the end of the program
execution (via END block). You should not use these functions other
than from within tests which should cleanup all the created
directories and files at the end of the test.
=head1 FUNCTIONS
=over
=item t_cmp()
t_cmp($received, $expected, $comment);
t_cmp() prints the values of I<$comment>, I<$expected> and
I<$received>. e.g.:
t_cmp(1, 1, "1 == 1?");
prints:
# testing : 1 == 1?
# expected: 1
# received: 1
then it returns the result of comparison of the I<$expected> and the
I<$received> variables. Usually, the return value of this function is
fed directly to the ok() function, like this:
ok t_cmp(1, 1, "1 == 1?");
the third argument (I<$comment>) is optional, mostly useful for
telling what the comparison is trying to do.
It is valid to use C<undef> as an expected value. Therefore:
my $foo;
t_cmp(undef, $foo, "undef == undef?");
will return a I<true> value.
You can compare any two data-structures with t_cmp(). Just make sure
that if you pass non-scalars, you have to pass their references. The
datastructures can be deeply nested. For example you can compare:
t_cmp({1 => [2..3,{5..8}], 4 => [5..6]},
{1 => [2..3,{5..8}], 4 => [5..6]},
"hash of array of hashes");
You can also compare the second argument against the first as a
regex. Use the C<qr//> function in the second argument. For example:
t_cmp("abcd", qr/^abc/, "regex compare");
will do:
"abcd" =~ /^abc/;
This function is exported by default.
=item t_filepath_cmp()
This function is used to compare two filepaths via t_cmp().
For non-Win32, it simply uses t_cmp() for the comparison,
but for Win32, Win32::GetLongPathName() is invoked to convert
the first two arguments to their DOS long pathname. This is useful
when there is a possibility the two paths being compared
are not both represented by their long or short pathname.
This function is exported by default.
=item t_debug()
t_debug("testing feature foo");
t_debug("test", [1..3], 5, {a=>[1..5]});
t_debug() prints out any datastructure while prepending C<#> at the
beginning of each line, to make the debug printouts comply with
C<Test::Harness>'s requirements. This function should be always used
for debug prints, since if in the future the debug printing will
change (e.g. redirected into a file) your tests won't need to be
changed.
the special global variable $Apache::TestUtil::DEBUG_OUTPUT can
be used to redirect the output from t_debug() and related calls
such as t_write_file(). for example, from a server-side test
you would probably need to redirect it to STDERR:
sub handler {
plan $r, tests => 1;
local $Apache::TestUtil::DEBUG_OUTPUT = \*STDERR;
t_write_file('/tmp/foo', 'bar');
...
}
left to its own devices, t_debug() will collide with the standard
HTTP protocol during server-side tests, resulting in a situation
both confusing difficult to debug. but STDOUT is left as the
default, since you probably don't want debug output under normal
circumstances unless running under verbose mode.
This function is exported by default.
=item t_write_test_lib()
t_write_test_lib($filename, @lines)
t_write_test_lib() creates a new file at I<$filename> or overwrites
the existing file with the content passed in I<@lines>. The file
is created in a temporary directory which is added to @INC at
test configuration time. It is intended to be used for creating
temporary packages for testing which can be modified at run time,
see the Apache::Reload unit tests for an example.
=item t_write_file()
t_write_file($filename, @lines);
t_write_file() creates a new file at I<$filename> or overwrites the
existing file with the content passed in I<@lines>. If only the
I<$filename> is passed, an empty file will be created.
If parent directories of C<$filename> don't exist they will be
automagically created.
The generated file will be automatically deleted at the end of the
program's execution.
This function is exported by default.
=item t_append_file()
t_append_file($filename, @lines);
t_append_file() is similar to t_write_file(), but it doesn't clobber
existing files and appends C<@lines> to the end of the file. If the
file doesn't exist it will create it.
If parent directories of C<$filename> don't exist they will be
automagically created.
The generated file will be registered to be automatically deleted at
the end of the program's execution, only if the file was created by
t_append_file().
This function is exported by default.
=item t_write_shell_script()
Apache::TestUtil::t_write_shell_script($filename, @lines);
Similar to t_write_file() but creates a portable shell/batch
script. The created filename is constructed from C<$filename> and an
appropriate extension automatically selected according to the platform
the code is running under.
It returns the extension of the created file.
=item t_write_perl_script()
Apache::TestUtil::t_write_perl_script($filename, @lines);
Similar to t_write_file() but creates a executable Perl script with
correctly set shebang line.
=item t_open_file()
my $fh = t_open_file($filename);
t_open_file() opens a file I<$filename> for writing and returns the
file handle to the opened file.
If parent directories of C<$filename> don't exist they will be
automagically created.
The generated file will be automatically deleted at the end of the
program's execution.
This function is exported by default.
=item t_mkdir()
t_mkdir($dirname);
t_mkdir() creates a directory I<$dirname>. The operation will fail if
the parent directory doesn't exist.
If parent directories of C<$dirname> don't exist they will be
automagically created.
The generated directory will be automatically deleted at the end of
the program's execution.
This function is exported by default.
=item t_rmtree()
t_rmtree(@dirs);
t_rmtree() deletes the whole directories trees passed in I<@dirs>.
This function is exported by default.
=item t_chown()
Apache::TestUtil::t_chown($file);
Change ownership of $file to the test's I<User>/I<Group>. This
function is noop on platforms where chown(2) is unsupported
(e.g. Win32).
=item t_is_equal()
t_is_equal($a, $b);
t_is_equal() compares any two datastructures and returns 1 if they are
exactly the same, otherwise 0. The datastructures can be nested
hashes, arrays, scalars, undefs or a combination of any of these. See
t_cmp() for an example.
If C<$b> is a regex reference, the regex comparison C<$a =~ $b> is
performed. For example:
t_is_equal($server_version, qr{^Apache});
If comparing non-scalars make sure to pass the references to the
datastructures.
This function is exported by default.
=item t_server_log_error_is_expected()
If the handler's execution results in an error or a warning logged to
the I<error_log> file which is expected, it's a good idea to have a
disclaimer printed before the error itself, so one can tell real
problems with tests from expected errors. For example when testing how
the package behaves under error conditions the I<error_log> file might
be loaded with errors, most of which are expected.
For example if a handler is about to generate a run-time error, this
function can be used as:
use Apache::TestUtil;
...
sub handler {
my $r = shift;
...
t_server_log_error_is_expected();
die "failed because ...";
}
After running this handler the I<error_log> file will include:
*** The following error entry is expected and harmless ***
[Tue Apr 01 14:00:21 2003] [error] failed because ...
When more than one entry is expected, an optional numerical argument,
indicating how many entries to expect, can be passed. For example:
t_server_log_error_is_expected(2);
will generate:
*** The following 2 error entries are expected and harmless ***
If the error is generated at compile time, the logging must be done in
the BEGIN block at the very beginning of the file:
BEGIN {
use Apache::TestUtil;
t_server_log_error_is_expected();
}
use DOES_NOT_exist;
After attempting to run this handler the I<error_log> file will
include:
*** The following error entry is expected and harmless ***
[Tue Apr 01 14:04:49 2003] [error] Can't locate "DOES_NOT_exist.pm"
in @INC (@INC contains: ...
Also see C<t_server_log_warn_is_expected()> which is similar but used
for warnings.
This function is exported by default.
=item t_server_log_warn_is_expected()
C<t_server_log_warn_is_expected()> generates a disclaimer for expected
warnings.
See the explanation for C<t_server_log_error_is_expected()> for more
details.
This function is exported by default.
=item t_client_log_error_is_expected()
C<t_client_log_error_is_expected()> generates a disclaimer for
expected errors. But in contrast to
C<t_server_log_error_is_expected()> called by the client side of the
script.
See the explanation for C<t_server_log_error_is_expected()> for more
details.
For example the following client script fails to find the handler:
use Apache::Test;
use Apache::TestUtil;
use Apache::TestRequest qw(GET);
plan tests => 1;
t_client_log_error_is_expected();
my $url = "/error_document/cannot_be_found";
my $res = GET($url);
ok t_cmp(404, $res->code, "test 404");
After running this test the I<error_log> file will include an entry
similar to the following snippet:
*** The following error entry is expected and harmless ***
[Tue Apr 01 14:02:55 2003] [error] [client 127.0.0.1]
File does not exist: /tmp/test/t/htdocs/error
When more than one entry is expected, an optional numerical argument,
indicating how many entries to expect, can be passed. For example:
t_client_log_error_is_expected(2);
will generate:
*** The following 2 error entries are expected and harmless ***
This function is exported by default.
=item t_client_log_warn_is_expected()
C<t_client_log_warn_is_expected()> generates a disclaimer for expected
warnings on the client side.
See the explanation for C<t_client_log_error_is_expected()> for more
details.
This function is exported by default.
=item t_catfile('a', 'b', 'c')
This function is essentially C<File::Spec-E<gt>catfile>, but
on Win32 will use C<Win32::GetLongpathName()> to convert the
result to a long path name (if the result is an absolute file).
The function is not exported by default.
=item t_catfile_apache('a', 'b', 'c')
This function is essentially C<File::Spec::Unix-E<gt>catfile>, but
on Win32 will use C<Win32::GetLongpathName()> to convert the
result to a long path name (if the result is an absolute file).
It is useful when comparing something to that returned by Apache,
which uses a Unix-style specification with forward slashes for
directory separators. The function is not exported by default.
=item t_start_error_log_watch(), t_finish_error_log_watch()
This pair of functions provides an easy interface for checking
the presence or absense of any particular message or messages
in the httpd error_log that were generated by the httpd daemon
as part of a test suite. It is likely, that you should proceed
this with a call to one of the t_*_is_expected() functions.
t_start_error_log_watch();
do_it;
ok grep {...} t_finish_error_log_watch();
Another usage case could be a handler that emits some debugging messages
to the error_log. Now, if this handler is called in a series of other
test cases it can be hard to find the relevant messages manually. In such
cases the following sequence in the test file may help:
t_start_error_log_watch();
GET '/this/or/that';
t_debug t_finish_error_log_watch();
=item t_start_file_watch()
Apache::TestUtil::t_start_file_watch('access_log');
This function is similar to C<t_start_error_log_watch()> but allows for
other files than C<error_log> to be watched. It opens the given file
and positions the file pointer at its end. Subsequent calls to
C<t_read_file_watch()> or C<t_finish_file_watch()> will read lines that
have been appended after this call.
A file name can be passed as parameter. If omitted
or undefined the C<error_log> is opened. Relative file name are
evaluated relative to the directory containing C<error_log>.
If the specified file does not exist (yet) no error is returned. It is
assumed that it will appear soon. In this case C<t_{read,finish}_file_watch()>
will open the file silently and read from the beginning.
=item t_read_file_watch(), t_finish_file_watch()
local $/ = "\n";
$line1=Apache::TestUtil::t_read_file_watch('access_log');
$line2=Apache::TestUtil::t_read_file_watch('access_log');
@lines=Apache::TestUtil::t_finish_file_watch('access_log');
This pair of functions reads the file opened by C<t_start_error_log_watch()>.
As does the core C<readline> function, they return one line if called in
scalar context, otherwise all lines until end of file.
Before calling C<readline> these functions do not set C<$/> as does
C<t_finish_error_log_watch>. So, if the file has for example a fixed
record length use this:
{
local $/=\$record_length;
@lines=t_finish_file_watch($name);
}
=item t_file_watch_for()
@lines=Apache::TestUtil::t_file_watch_for('access_log',
qr/condition/,
$timeout);
This function reads the file from the current position and looks for the
first line that matches C<qr/condition/>. If no such line could be found
until end of file the function pauses and retries until either such a line
is found or the timeout (in seconds) is reached.
In scalar or void context only the matching line is returned. In list
context all read lines are returned with the matching one in last position.
The function uses C<\n> and end-of-line marker and waits for complete lines.
The timeout although it can be specified with sub-second precision is not very
accurate. It is simply multiplied by 10. The result is used as a maximum loop
count. For the intented purpose this should be good enough.
Use this function to check for logfile entries when you cannot be sure that
they are already written when the test program reaches the point, for example
to check for messages that are written in a PerlCleanupHandler or a
PerlLogHandler.
ok t_file_watch_for 'access_log', qr/expected log entry/, 2;
This call reads the C<access_log> and waits for maximum 2 seconds for the
expected entry to appear.
=back
=head1 AUTHOR
Stas Bekman <stas@stason.org>,
Torsten Förtsch <torsten.foertsch@gmx.net>
=head1 SEE ALSO
perl(1)
=cut

View file

@ -0,0 +1,64 @@
# 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 Bundle::ApacheTest;
$VERSION = '0.02';
1;
__END__
=head1 NAME
Bundle::ApacheTest - A bundle to install all Apache-Test related modules
=head1 SYNOPSIS
perl -MCPAN -e 'install Bundle::ApacheTest'
=head1 CONTENTS
Crypt::SSLeay - For https support
Devel::CoreStack - For getting core stack info
Devel::Symdump - For, uh, dumping symbols
Digest::MD5 - Needed for Digest authentication
URI - There are URIs everywhere
Net::Cmd - For libnet
MIME::Base64 - Used in authentication headers
HTML::Tagset - Needed by HTML::Parser
HTML::Parser - Need by HTML::HeadParser
HTML::HeadParser - To get the correct $res->base
LWP - For libwww-perl
LWP::Protocol::https - LWP plug-in for the https protocol
IPC::Run3 - Used in Apache::TestSmoke
=head1 DESCRIPTION
This bundle lists all the CPAN modules used by Apache-Test.
=cut

View file

@ -0,0 +1,42 @@
use strict;
use lib qw(lib ../lib);
use warnings FATAL => 'all';
use Apache::TestRun ();
package MyTest;
use vars qw(@ISA);
@ISA = qw(Apache::TestRun);
#subclass new_test_config to add some config vars which will
#be replaced in generated config, see t/conf/extra.conf.in
#'make test' runs -clean by default, so to actually see the replacements:
#perl t/TEST apxs ...
#cat t/conf/extra.conf
#perl t/TEST -clean
sub new_test_config {
my $self = shift;
$self->{conf_opts}->{authname} = 'gold club';
$self->{conf_opts}->{allowed_users} = 'dougm sterling';
return $self->SUPER::new_test_config;
}
sub bug_report {
my $self = shift;
print <<EOI;
+-----------------------------------------------------+
| To report problems please refer to the SUPPORT file |
+-----------------------------------------------------+
EOI
}
MyTest->new->run(@ARGV);

View file

@ -0,0 +1,5 @@
use Apache::Test;
plan tests => 1;
ok (0, 'this test should never run');

View file

@ -0,0 +1,8 @@
use strict;
use warnings FATAL => 'all';
use Apache::Test;
plan tests => 1, skip_reason('testing all.t');
ok 1;

View file

@ -0,0 +1,5 @@
use Apache::Test;
plan tests => 1;
ok (0, 'this test should never run');

View file

@ -0,0 +1,8 @@
use strict;
use warnings FATAL => 'all';
use Apache::Test;
plan tests => 1, skip_reason('testing more than one all.t');
ok 1;

View file

@ -0,0 +1,22 @@
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestRequest;
# This test tests how good Apache-Test deals with bad coding practices
# of its users
plan tests => 1;
{
# passing $_ to a non-core function inside a foreach loop or
# similar, may affect $_ on return -- badly breaking things and
# making it hard to figure out where the problem is coming from.
#
# have_* macros localize $_ for these bad programming cases
# let's test that:
my @list = ('mod_dir');
my %modules = map { $_, have_module($_) } @list;
ok 1;
}

View file

@ -0,0 +1,16 @@
#!perl -wT
use strict;
use CGI;
use CGI::Cookie;
my %cookies = CGI::Cookie->fetch;
my $name = 'ApacheTest';
my $c = ! exists $cookies{$name}
? CGI::Cookie->new(-name=>$name, -value=>time)
: '';
print "Set-Cookie: $c\n" if $c;
print "Content-Type: text/plain\n\n";
print ($c ? 'new' : 'exists'), "\n";

View file

@ -0,0 +1,4 @@
use strict;
print "Content-Type: text/plain\n\n";
print $ENV{NextAvailablePort} || '';

View file

@ -0,0 +1,46 @@
#this file will be Include-d by @ServerRoot@/httpd.conf
#the subclass inside t/TEST added the authname and allowed_users variables
<IfModule mod_alias.c>
Redirect /redirect http://@ServerName@/redirected/
</IfModule>
<IfModule mod_perl.c>
<Location /TestMore__testpm>
SetHandler perl-script
<IfDefine APACHE2>
PerlResponseHandler TestMore::testpm
</IfDefine>
<IfDefine APACHE1>
PerlHandler TestMore::testpm
</IfDefine>
</Location>
<Location /TestMore__testmorepm>
SetHandler perl-script
<IfDefine APACHE2>
PerlResponseHandler TestMore::testmorepm
</IfDefine>
<IfDefine APACHE1>
PerlHandler TestMore::testmorepm
</IfDefine>
</Location>
</IfModule>
<IfModule @CGI_MODULE@>
ScriptAlias /cgi-bin/ "@ServerRoot@/cgi-bin/"
<Directory "@ServerRoot@/cgi-bin/">
AllowOverride None
Options +ExecCGI
</Directory>
# t/next_available_port.t
<IfModule mod_env.c>
SetEnv NextAvailablePort @NextAvailablePort@
</IfModule>
</IfModule>

View file

@ -0,0 +1,13 @@
use strict;
use warnings FATAL => qw(all);
use File::Spec ();
use lib (); # empty so we can calculate the lib to use
my @libs = (File::Spec->catfile('@ServerRoot@', 'response'),
File::Spec->catfile('@ServerRoot@', qw(.. lib)));
lib->import(@libs);
1;

View file

@ -0,0 +1,18 @@
# this test tests how a cookie jar can be passed (needs lwp)
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestRequest;
use Apache::TestUtil;
plan tests => 2, need [qw(CGI CGI::Cookie)],
need_cgi, need_lwp, need need_module('mod_alias.c');
Apache::TestRequest::user_agent( cookie_jar => {} );
my $url = '/cgi-bin/cookies.pl';
ok t_cmp GET_BODY($url), 'new', "new cookie";
ok t_cmp GET_BODY($url), 'exists', "existing cookie";

View file

@ -0,0 +1,145 @@
#!perl
use strict;
use warnings FATAL=>'all';
use Test ();
Test::plan tests=>47;
sub t {
my $p=$_[0];
no strict 'refs';
Test::ok defined &{$p."::ok"} && \&{$p."::ok"}==\&Test::ok,
1, "$p - ok";
Test::ok defined &{$p."::need"} && \&{$p."::need"}==\&Apache::Test::need,
1, "$p - need";
Test::ok defined &{$p."::plan"} && \&{$p."::plan"}==\&Apache::Test::plan,
1, "$p - plan";
}
sub tm {
my $p=$_[0];
no strict 'refs';
Test::ok defined &{$p."::ok"} && \&{$p."::ok"}==\&Test::More::ok,
1, "$p - ok";
Test::ok defined &{$p."::need"} && \&{$p."::need"}==\&Apache::Test::need,
1, "$p - need";
Test::ok defined &{$p."::plan"} && \&{$p."::plan"}==\&Apache::Test::plan,
1, "$p - plan";
}
{package X0; use Apache::Test;}
{package Y0; use Apache::Test qw/-withtestmore/;}
t 'X0';
tm 'Y0';
{package X1; use Apache::Test qw/:DEFAULT/;}
{package Y1; use Apache::Test qw/-withtestmore :DEFAULT/;}
t 'X1';
tm 'Y1';
{package X2; use Apache::Test qw/!:DEFAULT/;}
{package Y2; use Apache::Test qw/-withtestmore !:DEFAULT/;}
Test::ok !defined &X2::ok, 1, '!defined &X2::ok';
Test::ok !defined &X2::need, 1, '!defined &X2::need';
Test::ok !defined &X2::plan, 1, '!defined &X2::plan';
Test::ok !defined &Y2::ok, 1, '!defined &Y2::ok';
Test::ok !defined &Y2::need, 1, '!defined &Y2::need';
Test::ok !defined &Y2::plan, 1, '!defined &Y2::plan';
{package X3; use Apache::Test qw/plan/;}
{package Y3; use Apache::Test qw/-withtestmore plan/;}
Test::ok !defined &X3::ok, 1, '!defined &X3::ok';
Test::ok !defined &X3::need, 1, '!defined &X3::need';
Test::ok defined &X3::plan && \&X3::plan==\&Apache::Test::plan, 1, "X3 - plan";
Test::ok !defined &Y3::ok, 1, '!defined &Y3::ok';
Test::ok !defined &Y3::need, 1, '!defined &Y3::need';
Test::ok defined &Y3::plan && \&Y3::plan==\&Apache::Test::plan, 1, "Y3 - plan";
{package X4; use Apache::Test qw/need/;}
{package Y4; use Apache::Test qw/-withtestmore need/;}
Test::ok !defined &X4::ok, 1, '!defined &X4::ok';
Test::ok defined &X4::need && \&X4::need==\&Apache::Test::need, 1, "X4 - need";
Test::ok !defined &X4::plan, 1, '!defined &X4::plan';
Test::ok !defined &Y4::ok, 1, '!defined &Y4::ok';
Test::ok defined &Y4::need && \&Y4::need==\&Apache::Test::need, 1, "Y4 - need";
Test::ok !defined &Y4::plan, 1, '!defined &Y4::plan';
{package X5; use Apache::Test qw/ok/;}
{package Y5; use Apache::Test qw/-withtestmore ok/;}
Test::ok defined &X5::ok && \&X5::ok==\&Test::ok, 1, "X5 - ok";
Test::ok !defined &X5::need, 1, '!defined &X5::need';
Test::ok !defined &X5::plan, 1, '!defined &X5::plan';
Test::ok defined &Y5::ok && \&Y5::ok==\&Test::More::ok, 1, "Y5 - ok";
Test::ok !defined &Y5::need, 1, '!defined &Y5::need';
Test::ok !defined &Y5::plan, 1, '!defined &Y5::plan';
{package X6; use Apache::Test qw/ok need/;}
{package Y6; use Apache::Test qw/-withtestmore ok need/;}
Test::ok defined &X6::ok && \&X6::ok==\&Test::ok, 1, "X6 - ok";
Test::ok defined &X6::need && \&X6::need==\&Apache::Test::need, 1, "X6 - need";
Test::ok !defined &X6::plan, 1, '!defined &X6::plan';
Test::ok defined &Y6::ok && \&Y6::ok==\&Test::More::ok, 1, "Y6 - ok";
Test::ok defined &Y6::need && \&Y6::need==\&Apache::Test::need, 1, "Y6 - need";
Test::ok !defined &Y6::plan, 1, '!defined &Y6::plan';
my $warning;
{
local $SIG{__WARN__}=sub {$warning=join '', @_};
eval <<'EVAL';
package Z0;
use Apache::Test qw/:withtestmore/;
EVAL
}
Test::ok $warning, qr/^Ignoring import spec :withtestmore at/,
"Ignore import warning";
undef $warning;
{
local $SIG{__WARN__}=sub {$warning=join '', @_};
eval <<'EVAL';
package X0;
use Apache::Test qw/-withtestmore/;
EVAL
}
Test::ok $warning, qr/^Ignoring -withtestmore due to a previous call /,
"Ignore -withtestmore warning";
use Config ();
my $pio=$Config::Config{useperlio} ? '' : 'need perlio';
my $output;
Test::skip $pio, sub {
my @res;
{
local $Test::ntest=-19;
local $Test::planned=-42;
package Y2; # uses Apache::Test qw/-withtestmore !:DEFAULT/
# so nothing is exported
local *STDOUT;
open STDOUT, '>', \$output;
{
# suppress an 'uninitialized' warning in older perl versions
local $SIG{__WARN__}=sub {
warn $_[0]
unless $_[0]=~m!uninitialized\svalue\sin\sopen\b.+
Test/Builder\.pm!x;
};
Apache::Test::plan tests=>17;
}
Test::More::isnt "hugo", "erwin", "hugo is not erwin";
@res=($Test::ntest, $Test::planned);
Test::Builder->new->reset;
}
return "@res";
}, '-19 -42', '$Test::ntest, $Test::planned did not change';
Test::skip $pio, $output=~/^1\.\.17$/m;
Test::skip $pio, $output=~/^ok 1 - hugo is not erwin$/m;

View file

@ -0,0 +1,76 @@
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestUtil qw/t_start_file_watch
t_read_file_watch
t_finish_file_watch
t_write_file
t_append_file
t_catfile
t_cmp/;
plan tests => 11;
my $fn=t_catfile(Apache::Test::vars->{t_logs}, 'watch');
unlink $fn;
t_start_file_watch 'watch';
t_write_file $fn, "1\n2\n";
ok t_cmp [t_read_file_watch 'watch'], ["1\n", "2\n"],
"t_read_file_watch on previously non-existing file";
t_append_file $fn, "3\n4\n";
ok t_cmp [t_read_file_watch 'watch'], ["3\n", "4\n"],
"subsequent t_read_file_watch";
t_append_file $fn, "5\n6\n";
ok t_cmp [t_finish_file_watch 'watch'], ["5\n", "6\n"],
"subsequent t_finish_file_watch";
ok t_cmp [t_finish_file_watch 'watch'], ["1\n","2\n","3\n","4\n","5\n","6\n"],
"t_finish_file_watch w/o start";
ok t_cmp [t_read_file_watch 'watch'], ["1\n","2\n","3\n","4\n","5\n","6\n"],
"t_read_file_watch w/o start";
ok t_cmp [t_read_file_watch 'watch'], [],
"subsequent t_read_file_watch";
t_append_file $fn, "7\n8\n";
unlink $fn;
ok t_cmp [t_read_file_watch 'watch'], ["7\n","8\n"],
"subsequent t_read_file_watch file unlinked";
t_write_file $fn, "1\n2\n3\n4\n5\n6\n7\n8\n";
ok t_cmp [t_finish_file_watch 'watch'], [],
"subsequent t_finish_file_watch - new file exists but fh is cached";
t_start_file_watch 'watch';
ok t_cmp [t_read_file_watch 'watch'], [],
"t_read_file_watch at EOF";
# Make sure the file is closed before deleting it on Windows.
t_finish_file_watch 'watch' if $^O eq 'MSWin32';
unlink $fn;
t_start_file_watch 'watch';
t_write_file $fn, "1\n2\n3\n4\n5\n6\n7\n8\n";
{
local $/=\4;
ok t_cmp [scalar t_read_file_watch 'watch'], ["1\n2\n"],
"t_read_file_watch fixed record length / scalar context";
ok t_cmp [t_finish_file_watch 'watch'], ["3\n4\n","5\n6\n","7\n8\n"],
"t_finish_file_watch fixed record length";
}

View file

@ -0,0 +1,40 @@
#!perl
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestUtil qw/t_start_file_watch t_file_watch_for
t_cmp t_catfile t_append_file/;
plan tests => 5, need_fork;
my $fn=t_catfile(Apache::Test::vars->{t_logs}, 'watch');
unlink $fn;
t_start_file_watch 'watch';
my $pid;
select undef, undef, undef, 0.1 until defined($pid=fork);
unless ($pid) { # child
t_append_file $fn, "\nhuhu\n4 5 6 \nblabla\n";
for(1..3) {
select undef, undef, undef, 0.3;
t_append_file $fn, "$_ ";
}
t_append_file $fn, "\nhuhu\n4 5 6 \nblabla";
exit 0;
}
ok t_cmp t_file_watch_for('watch', qr/^1 2 3 $/, 2),
"1 2 3 \n", 'incomplete line';
my @lines=t_file_watch_for('watch', qr/^\d \d \d $/, 2);
ok t_cmp @lines, 2, '2 lines';
ok t_cmp $lines[0], "huhu\n", '1st line';
ok t_cmp $lines[1], "4 5 6 \n", 'found it';
ok t_cmp t_file_watch_for('watch', qr/^\d \d \d $/, 0.3),
undef, 'timeout';
waitpid $pid, 0;

View file

@ -0,0 +1,8 @@
# see the description in t/more/all.t
use strict;
use warnings FATAL => qw(all);
use Apache::TestRequest 'GET_BODY_ASSERT';
print GET_BODY_ASSERT "/TestMore__testpm";

View file

@ -0,0 +1,8 @@
# see the description in t/more/all.t
use strict;
use warnings FATAL => qw(all);
use Apache::TestRequest 'GET_BODY_ASSERT';
print GET_BODY_ASSERT "/TestMore__testmorepm";

View file

@ -0,0 +1,8 @@
# see the description in t/more/all.t
use strict;
use warnings FATAL => qw(all);
use Apache::TestRequest 'GET_BODY_ASSERT';
print GET_BODY_ASSERT "/TestMore__testpm";

View file

@ -0,0 +1,8 @@
# see the description in t/more/all.t
use strict;
use warnings FATAL => qw(all);
use Apache::TestRequest 'GET_BODY_ASSERT';
print GET_BODY_ASSERT "/TestMore__testmorepm";

View file

@ -0,0 +1,28 @@
# skip all the Test::More tests if Test::More is
# not of a sufficient version;
use strict;
use warnings FATAL => 'all';
use Apache::Test;
plan tests => 1, need need_min_module_version(qw(Test::More 0.48_01)),
need_module('mod_perl.c');
ok 1;
# the t/more/ directory is testing a few things.
#
# first, it is testing that the special
# Apache::Test qw(-withtestmore);
# import works, which allows Apache::Test to use
# Test::More as the backend (in place of Test.pm)
# for server-side tests.
#
# secondly, it is testing that we can intermix
# scripts that use Test.pm and Test::More as the
# backend, which was a bug that needed to be worked
# around in early implementations of -withtestmore.
# hence the reason for the specific ordering of the
# tests in t/more/.

View file

@ -0,0 +1,16 @@
# this test tests how a cookie jar can be passed (needs lwp)
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestRequest;
use Apache::TestUtil;
plan tests => 1, need need_cgi,
need_module('mod_env.c');
my $url = '/cgi-bin/next_available_port.pl';
my $port = GET_BODY($url) || '';
ok $port, qr/^\d+$/, "next available port number";

View file

@ -0,0 +1,17 @@
use strict;
use warnings FATAL => 'all';
use Apache::Test;
plan tests => 3;
my $config = Apache::Test::config();
ok $config;
my $server = $config->server;
ok $server;
ok $server->ping;

View file

@ -0,0 +1,23 @@
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestRequest;
plan tests => 6, need need_module('mod_alias.c'), need_lwp;
my $url = '/redirect';
# Allow request to be redirected.
ok my $res = GET $url;
ok ! $res->is_redirect;
# Don't let request be redirected.
ok $res = GET($url, redirect_ok => 0);
ok $res->is_redirect;
# Allow no more requests to be redirected.
Apache::TestRequest::user_agent(reset => 1,
requests_redirectable => 0);
ok $res = GET $url;
ok $res->is_redirect;

View file

@ -0,0 +1,28 @@
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestRequest;
plan tests => 9, \&need_lwp;
my $url = '/index.html';
ok GET_OK $url;
ok GET_RC $url;
ok GET_STR $url;
ok GET_BODY $url;
ok HEAD_OK $url;
ok HEAD_RC $url;
ok HEAD_STR $url;
ok GET_OK $url, username => 'dougm', password => 'XXXX'; #e.g. for auth
ok GET_OK $url, Referer => $0; #add headers
#post a string
#ok POST_OK $url, content => 'post body data';
#or key/value pairs (see HTTP::Request::Common
#ok POST_OK $url, [university => 'arizona', team => 'wildcats']

View file

@ -0,0 +1,21 @@
package TestMore::testmorepm;
use strict;
use warnings FATAL => qw(all);
use Test::More;
use Apache::Test qw(-withtestmore);
sub handler {
plan shift, tests => 2;
is (1, 1, 'called Test::More::is()');
like ('wow', qr/wow/, 'called Test::More::like()');
0;
}
1;

View file

@ -0,0 +1,18 @@
package TestMore::testpm;
use strict;
use warnings FATAL => qw(all);
use Apache::Test;
use Apache::TestUtil;
sub handler {
plan shift, tests => 1;
ok t_cmp(1, 1, 'called Apache::Test::ok()');
0;
}
1;

View file

@ -0,0 +1,168 @@
#!perl
use strict;
use warnings FATAL=>'all';
use Test ();
use Config ();
unless ($Config::Config{useperlio}) {
print "1..0 # need perlio\n";
exit 0;
}
Test::plan tests=>8;
my $output;
{
package X0;
use Apache::Test;
local ($Test::planned, $Test::ntest, %Test::todo);
local *STDOUT;
open STDOUT, '>', \$output;
local $ENV{HTTPD_TEST_SUBTESTS}="";
plan tests=>3;
sok {1};
sok {1};
sok {1};
}
Test::ok $output=~/^ok 1$/m &&
$output=~/^ok 2$/m &&
$output=~/^ok 3$/m;
{
package Y0;
use Apache::Test qw/-withtestmore/;
local *STDOUT;
open STDOUT, '>', \$output;
local $ENV{HTTPD_TEST_SUBTESTS}="";
plan tests=>3;
sok {1};
sok {1};
sok {1};
}
Test::ok $output=~/^ok 1$/m &&
$output=~/^ok 2$/m &&
$output=~/^ok 3$/m;
{
package X0;
local ($Test::planned, $Test::ntest, %Test::todo);
local *STDOUT;
open STDOUT, '>', \$output;
local $ENV{HTTPD_TEST_SUBTESTS}="1 3";
plan tests=>3;
sok {1};
sok {1};
sok {1};
}
Test::ok $output=~/^ok 1$/m &&
$output=~/^ok 2 # skip skipping this subtest$/mi &&
$output=~/^ok 3$/m;
{
package Y0;
local *STDOUT;
open STDOUT, '>', \$output;
local $ENV{HTTPD_TEST_SUBTESTS}="1 3";
plan tests=>3;
sok {1};
sok {1};
sok {1};
}
Test::ok $output=~/^ok 1$/m &&
$output=~/^ok 2 # skip skipping this subtest$/mi &&
$output=~/^ok 3$/m;
{
package X0;
local ($Test::planned, $Test::ntest, %Test::todo);
local *STDOUT;
open STDOUT, '>', \$output;
local $ENV{HTTPD_TEST_SUBTESTS}="";
plan tests=>4;
sok {1};
sok {ok 1; 1} 2;
sok {1};
}
Test::ok $output=~/^ok 1$/m &&
$output=~/^ok 2$/m &&
$output=~/^ok 3$/m &&
$output=~/^ok 4$/m;
{
package Y0;
local *STDOUT;
open STDOUT, '>', \$output;
local $ENV{HTTPD_TEST_SUBTESTS}="";
plan tests=>4;
sok {1};
sok {ok 1, "erwin"} 2;
sok {1};
}
Test::ok $output=~/^ok 1$/m &&
$output=~/^ok 2 - erwin$/m &&
$output=~/^ok 3$/m &&
$output=~/^ok 4$/m;
{
package X0;
local ($Test::planned, $Test::ntest, %Test::todo);
local *STDOUT;
open STDOUT, '>', \$output;
local $ENV{HTTPD_TEST_SUBTESTS}="1 4";
plan tests=>4;
sok {1};
sok {ok 1; 1} 2;
sok {1};
}
Test::ok $output=~/^ok 1$/m &&
$output=~/^ok 2 # skip skipping this subtest$/mi &&
$output=~/^ok 3 # skip skipping this subtest$/mi &&
$output=~/^ok 4$/m;
{
package Y0;
local *STDOUT;
open STDOUT, '>', \$output;
local $ENV{HTTPD_TEST_SUBTESTS}="1 4";
plan tests=>4;
sok {1};
sok {ok 1} 2;
sok {1};
}
Test::ok $output=~/^ok 1$/m &&
$output=~/^ok 2 # skip skipping this subtest$/mi &&
$output=~/^ok 3 # skip skipping this subtest$/mi &&
$output=~/^ok 4$/m;

204
debian/perl-framework/LICENSE vendored Normal file
View file

@ -0,0 +1,204 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.

58
debian/perl-framework/Makefile.PL vendored Normal file
View file

@ -0,0 +1,58 @@
use ExtUtils::MakeMaker;
use 5.005;
use lib qw(Apache-Test/lib);
use Apache::Test5005compat;
use Apache::TestMM qw(test clean);
use Apache::TestReport ();
use Apache::TestSmoke ();
use Apache::TestRun ();
use File::Find qw(finddepth);
my @scripts = ();
finddepth(sub {
return unless /^(?!.#)(.*?\.pl)\.PL$/;
push @scripts, "$File::Find::dir/$1";
}, '.');
Apache::TestMM::filter_args();
# Temporary workaround to allow passing
# arguments to "perl Makefile.PL"
# that should go to t/TEST but are not yet
# supported in an Apache::Test release.
# Code borrowed from Apache::TestMM::filter_args().
my %local_args = (
limitrequestline => 'Value for LimitRequestLine',
limitrequestlinex2 => 'Twice the value for LimitRequestLine',
);
my($argv, $args_vars) = Apache::TestConfig::filter_args(\@ARGV, \%local_args);
@ARGV = @$argv;
# Merge given vars with default values
my %local_vars = (
limitrequestline => '128',
limitrequestlinex2 => '256',
);
map {$local_vars{$_} = $args_vars->{$_}} keys %$args_vars;
push(@Apache::TestMM::Argv, %local_vars);
for my $script (@scripts) {
Apache::TestMM::generate_script($script);
}
for my $util (qw(Report Smoke Run)) {
my $class = "Apache::Test${util}";
$class->generate_script;
}
WriteMakefile(
NAME => 'httpd-test',
VERSION => '0.01',
clean => { FILES => "@scripts" },
);

49
debian/perl-framework/Misc.pm vendored Normal file
View file

@ -0,0 +1,49 @@
# 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 Misc;
use Apache::Test;
use Apache::TestRequest;
use Apache::TestUtil;
use Apache::TestConfig ();
use Time::HiRes qw(usleep);
use strict;
use warnings FATAL => 'all';
BEGIN {
# Just a bunch of useful subs
}
sub cwait
{
my $condition = shift;
my $wait = shift || 2;
my $inc = shift || 50;
my $timer = time() + $wait;
while (! eval $condition) {
usleep($inc);
last if (time() >= $timer);
}
if ( eval $condition ) {
return 1;
} else {
return 0;
}
}
1;
__END__

6
debian/perl-framework/NOTICE vendored Normal file
View file

@ -0,0 +1,6 @@
Apache HTTP Server Test Framework
Copyright 2020 The Apache Software Foundation.
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

243
debian/perl-framework/README vendored Normal file
View file

@ -0,0 +1,243 @@
Testing Apache with the Perl Test Harness
Prerequisites
-------------
These two modules must first be installed;
- perl-ExtUtils-MakeMaker
- perl-Test
You'll need to install the CPAN modules listed in:
Apache-Test/lib/Bundle/ApacheTest.pm
All you have to do to install them all in one shot is:
perl -MCPAN -e 'install Bundle::ApacheTest'
Which are also available in one tarball here:
http://perl.apache.org/~dougm/httpd-test-bundle-0.02.tar.gz
Note: Crypt::SSLeay requires OpenSSL to be installed (only required
for t/TEST -ssl): http://www.openssl.org/
More accurate results may be obtained by using the same openssl command
line and libraries as consumed by APR-util and mod_ssl, due to X509
formatting behavior differences.
For an extensive documentation see
http://perl.apache.org/docs/general/testing/testing.html
or
http://svn.apache.org/viewvc/perl/modperl/docs/trunk/src/docs/general/testing/testing.pod
To run the tests for all Apache web server modules, some additional
CPAN modules will be required. If the tests don't work, make sure
that you have up to date versions of each of these perl modules:
```
cpan App::cpanminus
cpanm Bundle::ApacheTest \
HTTP::DAV DateTime Time::HiRes \
Test::Harness Crypt::SSLeay Net::SSLeay IO::Socket::SSL \
IO::Socket::IP IO::Select LWP::Protocol::https AnyEvent \
AnyEvent::WebSocket::Client LWP::Protocol::AnyEvent::http FCGI
```
Quick Start
-----------
If you don't care how it works and just want to run the tests, here's
how you go about doing that.
1. You need an installation of Apache. (1.3.x thru trunk)
2. Any DSOs you wish to use should be configured in that Apache's
httpd.conf (the test harness will pick this configuration up)
3. Setup:
perl Makefile.PL -apxs /path/to/apache/bin/apxs
4. Run the tests:
t/TEST
5. Evaluate test output.
Getting a little deeper
-----------------------
The test harness will run every .t file under the t/ directory. So
let's say you only want to run the tests for PHP. Do this:
t/TEST -httpd /path/to/apache/bin/httpd t/php
That will start the test server, run the .t tests under t/php and shut
down the test server. You can also control each of these steps.
This will start the test server:
t/TEST -httpd /path/to/apache/bin/httpd -start
This will run the PHP tests in the test environment:
t/TEST t/php
This will stop the test server:
t/TEST -stop
This will run the server under gdb (using -X):
t/TEST -d gdb
Note: At this point, you have a working test environment. You can
look in t/conf for the test server configuration files. These are
generated by the test harness. Once you have a working test
environment, you do not need to specify 'httpd' on the t/TEST command
line. For instance, to start the server up again, the command
t/TEST -start
would be sufficient.
Running Regression Tests
------------------------
For a full regression test, you should have all modules loaded. Build the server
with
configure --enable-modules=reallyall --enable-load-all-modules ...
among other things. Edit the generated httpd.conf and comment all mpm modules
that you do not want. Run "t/TEST -clean" again.
You will see some
skipped: cannot find module 'XXX'
as not all modules are in every apache release (but the tests run for all).
All in all, some >4k tests will run and the result needs to be: PASS
Trouble Shooting
----------------
If you have a "PASS" at the end of "t/TEST", congratulations! If not, this
sections gives some advise in order to find the cause. Feel free to expand
this to make life easier for others.
0. If your test startup hangs forever in "waiting for server to warm up", but
the test server is reachable under port 8529, you might be the victim of
ipv4/6 confusion. The default servername configured is "localhost" and
some operating systems define 127.0.0.1 *as well as* ::1 in /etc/hosts.
If the test server listens only on 0.0.0.0 it might not answer requests to
::1 and that causes the test startup to hang.
Solution: comment the ::1 line in /etc/hosts and see if that improves matters.
1. Run "t/TEST -clean" every time you change something in your Apache
configuration. The test suite picks up certain things from your installed
httpd.conf (such as LoadModule statements) and will not see your changes
unless you clean it.
2. Failures in proxy.t may originate from the fact that the test script cannot
open the specified port. This happens on some machines if you abort a test
run and the socket is not properly shut down. Check if the error goes
away after a reboot. (proxy.t tests are slow, so chances you interrupt tests
at that point are good.)
3. Failures in access.t may result from reverse lookups not working or giving
other answers than expected. In the cause 0 above, if the test client
connects via 127.0.0.1, a "Grant for localhost" might resolve to "::1"
and therefore will not match the access rules of the tests.
Solution: check that your servername is 'localhost' (which is
the default) and that it *always* resolves to 127.0.0.1.
4. If some ssl test cases fail, especially when t/ssl/proxy.t fails, the
reason can be mismatches between your installed SSL library and the one
httpd uses. The "openssl" binary found in your $PATH is used to create
binary setup files by t/TEST. If another version of openssl then tries
to read these from your Apache server process, it might fail.
Try the following:
> t/TEST -clean
> PATH=<bin dir of correct openssl>:$PATH t/TEST
If a lot of ssl tests fail, check in the error log for the presence of
a certificate validation error. If you find it, check the expiration date
of the TLS/SSL certificates used by the tests, they might be expired.
Running TEST -clean should delete the old ssl certificates, so they'll be
regenerated during the next run.
5. If you see failures in the modules/h2.t test cases, please notify the dev
mailing list with a description of your setup. These tests are quite young,
currently only valid in 2.4.x and later and interact with quite some other
modules as well as Openssl versions installed. Some tests require mod_ssl
so make sure to load it in the httpd conf.
6. Segmentation faults and core dumps occurring while executing the test suite
might indicate a real problem but always run again the tests after
a clean make install to avoid inconsistencies from old objects.
7. If you see error messages like "Parse errors: Bad plan.
You planned X tests but ran Y." it usually means that you are missing
a perl module or the tested httpd module depends on another one
not loaded in the httpd config.
8. If you see SSL certificate errors, remove t/conf/ssl/ca prior to
t/TEST -clean
9. perl 5.28 in MacOS homebrew seems to hang the test suite. Invoking
/usr/bin/perl Makefile.PL -apxs ... will cause an older perl to be used.
Smoking Tests
-------------
Sometimes it's possible that the test is passing properly for the
first time, when it's run for the first time in the thread. But when
you run it again, the test might fail. It's important to run the
repetition smoke testing. For example to repeat the tests 5 times you
can run:
t/SMOKE -times=5
It's also possible that a test will pass when it's run after a
particular test, but if moved to run after a different state it may
fail. For this reason by default the tests run in random order.
Since it's important to be able to reproduce the problem with the
random testing, whenever -order=random is used, the used seed is
printed to STDERR. Which can be then fed into the future tests with:
via APACHE_TEST_SEED environment variable.
By adding the option -order=repeat, the tests will be run in
alphabetical order.
Combining these two important smoke testing techniques, one can run
tests with:
t/SMOKE -times=N -order=(repeat|random)
For example, to run the mod_rewrite tests 5 times, one would:
t/SMOKE -times=5 -verbose t/modules/rewrite.t
So the tests can be repeated N times, and run in the following three
modes:
- randomize all tests
- repeat the whole tests suite N times
For configuration options and default settings run:
t/SMOKE -help
For more information refer to the Apache::TestSmoke manpage.
Test Environment Configuration
------------------------------
The test server is configured with conf files like any normal Apache
server. The tricky part is those conf files are generated by the
harness just prior to starting the server. t/conf/httpd.conf is
generated by t/conf/httpd.conf.in. If that does not exist, the
harness will generate a working configuration and will include
LoadModule (and AddModule for Apache 1.3) directives from the
httpd.conf associated with the httpd binary you are using for testing.
If t/conf/extra.conf.in exists, t/conf/extra.conf will be generated
from that, and an Include directive for that file will be put in the
generated t/conf/httpd.conf. t/conf/apache_test_config.pm is
generated from the test configuration. It contains all the
information about the configuration of your test server. You can
access this information in test scripts by:
my $env = Apache::TestConfig->thaw;
Apache::TestConfig access apache_test_config.pm and returns a hash
reference with all the information. Look through
apache_test_config.pm, it's a lot of stuff. Once these conf files are
generated, you have a working test environment, and they must be
'cleaned' if you wish to make changes to them. To clean the
environment:
t/TEST -clean
(Now you will have to specify your httpd binary when starting back up
again.)
More Information
----------------
For more information on using the test harness and writing tests, see
the README in Apache-Test and the examples in Apache-Test/t.
The test harness was originally written by Doug MacEachern and is
discussed on the httpd dev mailing list (dev@httpd.apache.org).
It is also included in modperl-2.0 source along with tests for
modperl-2.0.

33
debian/perl-framework/STATUS vendored Normal file
View file

@ -0,0 +1,33 @@
httpd-test/perl-framework STATUS: -*-text-*-
Last modified at [$Date: 2018-10-10 18:23:46 +0400 (Wed, 10 Oct 2018) $]
Stuff to do:
* finish the t/TEST exit code issue (ORed with 0x2C if
framework failed)
* change existing tests that frob the DocumentRoot (e.g.,
t/modules/access.t) to *not* do that; instead, have
Makefile.PL prepare appropriate subdirectory configs
for them. Why? So t/TEST can be used to test a
remote server.
* problems with -d perl mode, doesn't work as documented
Message-ID: <3BD10479.2020506@stason.org>
Date: Sat, 20 Oct 2001 12:58:33 +0800
Subject: Re: perldb
Tests to be written:
* t/apache
- simulations of network failures (incomplete POST bodies,
chunked and unchunked; missing POST bodies; slooow
client connexions, such as taking 1 minute to send
1KiB; ...)
* t/modules/autoindex
- something seems possibly broken with inheritance on 2.0
* t/ssl
- SSLPassPhraseDialog exec:
- SSLRandomSeed exec:

9
debian/perl-framework/build/config.pl vendored Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/perl -w
use strict;
use FindBin qw($Bin);
use lib "$Bin/../Apache-Test/lib";
use Apache::TestConfig ();
print Apache::TestConfig::as_string();

View file

@ -0,0 +1,172 @@
#if CONFIG_FOR_HTTPD_TEST
Alias /authany @DocumentRoot@
<Location /authany>
require user any-user
AuthType Basic
AuthName authany
<IfDefine !APACHE1>
<IfVersion >= 2.3>
AuthBasicProvider any
</IfVersion>
</IfDefine>
</Location>
#endif
#include "ap_mmn.h"
/* do not accept empty "" strings */
#define strtrue(s) (s && *s)
#if AP_MODULE_MAGIC_AT_LEAST(20060110, 0)
#include "ap_provider.h"
#include "mod_auth.h"
static authn_status authn_check_password(request_rec *r, const char *user,
const char *password)
{
return strtrue(r->user) && strcmp(r->user, "guest") == 0
? AUTH_GRANTED : AUTH_DENIED;
}
static const authn_provider authn_any_provider =
{
&authn_check_password
};
static authz_status any_check_authorization(request_rec *r,
const char *requirement,
const void *dummy)
{
#if AP_MODULE_MAGIC_AT_LEAST(20100714,0)
if (!r->user)
return AUTHZ_DENIED_NO_USER;
#endif
return strtrue(r->user) && strcmp(requirement, "any-user") == 0
? AUTHZ_GRANTED : AUTHZ_DENIED;
}
static const authz_provider authz_any_provider =
{
&any_check_authorization
};
static void extra_hooks(apr_pool_t *p)
{
ap_register_provider(p, AUTHN_PROVIDER_GROUP,
"any", "0", &authn_any_provider);
ap_register_provider(p, AUTHZ_PROVIDER_GROUP,
"user", "0", &authz_any_provider);
}
#define APACHE_HTTPD_TEST_EXTRA_HOOKS extra_hooks
#include "apache_httpd_test.h"
#else /* < 2.3 */
#ifdef APACHE2
#include "apr_pools.h"
static void extra_hooks(apr_pool_t *);
#define APACHE_HTTPD_TEST_EXTRA_HOOKS extra_hooks
#else
#define APACHE_HTTPD_TEST_HOOK_ORDER APR_HOOK_FIRST
#define APACHE_HTTPD_TEST_CHECK_USER_ID authany_handler
#define APACHE_HTTPD_TEST_AUTH_CHECKER require_any_user
#endif
#include "apache_httpd_test.h"
static int require_any_user(request_rec *r)
{
const apr_array_header_t *requires = ap_requires(r);
require_line *rq;
int x;
if (!requires) {
return DECLINED;
}
rq = (require_line *) requires->elts;
for (x = 0; x < requires->nelts; x++) {
const char *line, *requirement;
line = rq[x].requirement;
requirement = ap_getword(r->pool, &line, ' ');
if ((strcmp(requirement, "user") == 0) &&
(strcmp(line, "any-user") == 0))
{
return OK;
}
}
return DECLINED;
}
static int authany_handler(request_rec *r)
{
const char *sent_pw;
int rc = ap_get_basic_auth_pw(r, &sent_pw);
char *user;
if (rc != OK) {
return rc;
}
if (require_any_user(r) != OK) {
return DECLINED;
}
#ifdef APACHE1
user = r->connection->user;
#endif
#ifdef APACHE2
user = r->user;
#endif
if (!(strtrue(user) && strtrue(sent_pw))) {
ap_note_basic_auth_failure(r);
#ifdef APACHE1
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Both a username and password must be provided");
#endif
#ifdef APACHE2
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"Both a username and password must be provided");
#endif
return HTTP_UNAUTHORIZED;
}
return OK;
}
#ifdef APACHE2
static void extra_hooks(apr_pool_t *p)
{
/* mod_authany and mod_ssl both specify APR_HOOK_FIRST as the
* ordering of their check-user-id hooks.
* mod_ssl's must run before mod_authany because it may need to
* generate the Basic auth information based on the certificate.
*/
static const char * const modssl_runs_before[] = {"mod_ssl.c", NULL};
ap_hook_check_user_id(authany_handler, modssl_runs_before, NULL,
APR_HOOK_FIRST);
ap_hook_auth_checker(require_any_user, NULL, NULL, APR_HOOK_FIRST);
}
#endif
#endif
APACHE_HTTPD_TEST_MODULE(authany);

View file

@ -0,0 +1,54 @@
#define HTTPD_TEST_REQUIRE_APACHE 2
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_log.h"
#include "ap_config.h"
/*
* in real life we'd never allow the client to configure filters.
* the purpose of this module is to let .t tests configure filters
* this allows to test non-filtered and filtered requests without
* duplicating lots of test configuration
*/
static int client_add_filter_header(void *data,
const char *key,
const char *val)
{
request_rec *r = (request_rec *)data;
if (strcasecmp(key, "X-AddInputFilter") == 0) {
ap_add_input_filter(val, NULL, r, r->connection);
}
else if (strcasecmp(key, "X-AddOutputFilter") == 0) {
ap_add_output_filter(val, NULL, r, r->connection);
}
return 1;
}
static void client_add_filter_insert(request_rec *r)
{
apr_table_do(client_add_filter_header, (void*)r,
r->headers_in, NULL);
}
static void client_add_filter_register_hooks(apr_pool_t *p)
{
ap_hook_insert_filter(client_add_filter_insert,
NULL, NULL, APR_HOOK_LAST);
}
module AP_MODULE_DECLARE_DATA client_add_filter_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
NULL, /* table of config file commands */
client_add_filter_register_hooks /* register hooks */
};

Some files were not shown because too many files have changed in this diff Show more