Apache-Test
view release on metacpan or search on metacpan
lib/Apache/TestRun.pm view on Meta::CPAN
# 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::TestRun;
use strict;
use warnings FATAL => 'all';
use Apache::Test ();
use Apache::TestMM ();
use Apache::TestConfig ();
use Apache::TestConfigC ();
use Apache::TestRequest ();
use Apache::TestHarness ();
use Apache::TestTrace;
use Cwd;
use ExtUtils::MakeMaker;
use File::Find qw(finddepth);
use File::Path;
use File::Spec::Functions qw(catfile catdir canonpath);
use File::Basename qw(basename dirname);
use Getopt::Long qw(GetOptions);
use Config;
use constant IS_APACHE_TEST_BUILD => Apache::TestConfig::IS_APACHE_TEST_BUILD;
use constant STARTUP_TIMEOUT => 300; # secs (good for extreme debug cases)
use subs qw(exit_shell exit_perl);
my $orig_command;
my $orig_cwd;
my $orig_conf_opts;
my %core_files = ();
my @std_run = qw(start-httpd run-tests stop-httpd);
my @others = qw(verbose configure clean help ssl http11 bugreport
save no-httpd one-process);
my @flag_opts = (@std_run, @others);
my @string_opts = qw(order trace);
my @ostring_opts = qw(proxy ping);
my @debug_opts = qw(debug);
my @list_opts = qw(preamble postamble breakpoint);
my @hash_opts = qw(header);
my @help_opts = qw(clean help);
my @request_opts = qw(get post head);
my @exit_opts_no_need_httpd = (@help_opts);
my @exit_opts_need_httpd = (@debug_opts, qw(ping));
my %usage = (
'start-httpd' => 'start the test server',
'run-tests' => 'run the tests',
'order=mode' => 'run the tests in one of the modes: ' .
'(repeat|random|SEED)',
'stop-httpd' => 'stop the test server',
'no-httpd' => 'run the tests without configuring or starting httpd',
'verbose[=1]' => 'verbose output',
'configure' => 'force regeneration of httpd.conf ' .
' (tests will not be run)',
'clean' => 'remove all generated test files',
'help' => 'display this message',
'bugreport' => 'print the hint how to report problems',
'preamble' => 'config to add at the beginning of httpd.conf',
'postamble' => 'config to add at the end of httpd.conf',
'ping[=block]' => 'test if server is running or port in use',
'debug[=name]' => 'start server under debugger name (gdb, ddd, etc.)',
'breakpoint=bp' => 'set breakpoints (multiply bp can be set)',
'header' => "add headers to (" .
join('|', @request_opts) . ") request",
'http11' => 'run all tests with HTTP/1.1 (keep alive) requests',
'ssl' => 'run tests through ssl',
'proxy' => 'proxy requests (default proxy is localhost)',
'trace=T' => 'change tracing default to: warning, notice, ' .
'info, debug, ...',
'one-process' => 'run the server in single process mode',
(map { $_, "\U$_\E url" } @request_opts),
);
sub fixup {
#make sure we use an absolute path to perl
#else Test::Harness uses the perl in our PATH
#which might not be the one we want
$^X = $Config{perlpath} unless -e $^X;
}
# if the test suite was aborted because of a user-error we don't want
# to call the bugreport and invite users to submit a bug report -
# after all it's a user error. but we still want the program to fail,
# so raise this flag in such a case.
my $user_error = 0;
sub user_error {
my $self = shift;
$user_error = shift if @_;
$user_error;
}
sub new {
my $class = shift;
my $self = bless {
tests => [],
@_,
}, $class;
$self->fixup;
$self;
}
#split arguments into test files/dirs and options
#take extra care if -e, the file matches /\.t$/
# if -d, the dir contains .t files
#so we dont slurp arguments that are not tests, example:
# httpd $HOME/apache-2.0/bin/httpd
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 '/') {
lib/Apache/TestRun.pm view on Meta::CPAN
$server->stop(1) if $opts->{'start-httpd'};
$server->failed_msg("error running tests");
exit_perl 0;
};
$SIG{INT} = sub {
if ($caught_sig_int++) {
warning "\ncaught SIGINT";
exit_perl 0;
}
warning "\nhalting tests";
$server->stop if $opts->{'start-httpd'};
exit_perl 0;
};
#try to make sure we scan for core no matter what happens
#must eval "" to "install" this END block, otherwise it will
#always run, a subclass might not want that
eval 'END {
return unless is_parent(); # because of fork
$self ||=
Apache::TestRun->new(test_config => Apache::TestConfig->thaw);
{
local $?; # preserve the exit status
eval {
$self->scan_core;
};
}
$self->try_bug_report();
}';
die "failed: $@" if $@;
}
sub try_bug_report {
my $self = shift;
if ($? && !$self->user_error &&
$self->{opts}->{bugreport} && $self->can('bug_report')) {
$self->bug_report;
}
}
#throw away cached config and start fresh
sub refresh {
my $self = shift;
$self->opt_clean(1);
$self->{conf_opts}->{save} = delete $self->{conf_opts}->{thaw} || 1;
$self->{test_config} = $self->new_test_config()->httpd_config;
$self->{test_config}->{server}->{run} = $self;
$self->{server} = $self->{test_config}->server;
}
sub configure_opts {
my $self = shift;
my $save = shift;
my $refreshed = 0;
my($test_config, $opts) = ($self->{test_config}, $self->{opts});
$test_config->{vars}->{scheme} =
$opts->{ssl} ? 'https' :
$self->{conf_opts}->{scheme} || 'http';
if ($opts->{http11}) {
$ENV{APACHE_TEST_HTTP11} = 1;
}
# unless we are already reconfiguring, check for .conf.in files changes
if (!$$save &&
(my @reasons =
$self->{test_config}->need_reconfiguration($self->{conf_opts}))) {
warning "forcing re-configuration:";
warning "\t- $_." for @reasons;
unless ($refreshed) {
$self->refresh;
$refreshed = 1;
$test_config = $self->{test_config};
}
}
# unless we are already reconfiguring, check for -proxy
if (!$$save && exists $opts->{proxy}) {
my $max = $test_config->{vars}->{maxclients};
$opts->{proxy} ||= 'on';
#if config is cached and MaxClients == 1, must reconfigure
if (!$$save and $opts->{proxy} eq 'on' and $max == 1) {
$$save = 1;
warning "server is reconfigured for proxy";
unless ($refreshed) {
$self->refresh;
$refreshed = 1;
$test_config = $self->{test_config};
}
}
$test_config->{vars}->{proxy} = $opts->{proxy};
}
else {
$test_config->{vars}->{proxy} = 'off';
}
return unless $$save;
my $preamble = sub { shift->preamble($opts->{preamble}) };
my $postamble = sub { shift->postamble($opts->{postamble}) };
$test_config->preamble_register($preamble);
$test_config->postamble_register($postamble);
}
sub pre_configure { }
sub configure {
my $self = shift;
if ($self->{opts}->{'no-httpd'}) {
warning "skipping httpd configuration";
return;
}
# create the conf dir as early as possible
$self->{test_config}->prepare_t_conf();
my $save = \$self->{conf_opts}->{save};
$self->configure_opts($save);
my $config = $self->{test_config};
unless ($$save) {
my $addr = \$config->{vars}->{remote_addr};
my $remote_addr = $config->our_remote_addr;
unless ($$addr eq $remote_addr) {
warning "local ip address has changed, updating config cache";
$$addr = $remote_addr;
}
#update minor changes to cached config
#without complete regeneration
#for example this allows switching between
#'t/TEST' and 't/TEST -ssl'
$config->sync_vars(qw(scheme proxy remote_addr));
return;
}
my $test_config = $self->{test_config};
$test_config->sslca_generate;
$test_config->generate_ssl_conf if $self->{opts}->{ssl};
$test_config->cmodules_configure;
$test_config->generate_httpd_conf;
$test_config->save;
}
sub try_exit_opts {
my $self = shift;
my @opts = @_;
for (@opts) {
next unless exists $self->{opts}->{$_};
my $method = "opt_$_";
my $rc = $self->$method();
exit_perl $rc if $rc;
}
if ($self->{opts}->{'stop-httpd'}) {
my $ok = 1;
if ($self->{server}->ping) {
$ok = $self->{server}->stop;
$ok = $ok < 0 ? 0 : 1; # adjust to 0/1 logic
}
else {
warning "server $self->{server}->{name} is not running";
# cleanup a stale pid file if found
my $pid_file = $self->{test_config}->{vars}->{t_pid_file};
unlink $pid_file if -e $pid_file;
}
exit_perl $ok;
}
}
sub start {
my $self = shift;
my $opts = $self->{opts};
my $server = $self->{server};
#if t/TEST -d is running make sure we don't try to stop/start the server
my $file = $server->debugger_file;
if (-e $file and $opts->{'start-httpd'}) {
if ($server->ping) {
warning "server is running under the debugger, " .
"defaulting to -run";
$opts->{'start-httpd'} = $opts->{'stop-httpd'} = 0;
}
else {
warning "removing stale debugger note: $file";
unlink $file;
}
}
$self->check_runtime_user();
if ($opts->{'start-httpd'}) {
exit_perl 0 unless $server->start;
}
elsif ($opts->{'run-tests'}) {
my $is_up = $server->ping
( run in 1.889 second using v1.01-cache-2.11-cpan-acf6aa7dc9e )