stas 2004/01/07 09:51:21
Modified: perl-framework/Apache-Test Changes Makefile.PL
perl-framework/Apache-Test/lib/Apache TestConfig.pm
TestRun.pm
Log:
implementing custom interactive and non-interactive (with the -save
option) reusable configuration for -httpd, -apxs, -user, -group, and
-port
Revision Changes Path
1.76 +4 -0 httpd-test/perl-framework/Apache-Test/Changes
Index: Changes
===================================================================
RCS file: /home/cvs/httpd-test/perl-framework/Apache-Test/Changes,v
retrieving revision 1.75
retrieving revision 1.76
diff -u -u -r1.75 -r1.76
--- Changes 22 Dec 2003 19:51:02 -0000 1.75
+++ Changes 7 Jan 2004 17:51:21 -0000 1.76
@@ -8,6 +8,10 @@
=item 1.08_dev
+implementing custom interactive and non-interactive (with the -save
+option) reusable configuration for -httpd, -apxs, -user, -group, and
+-port [Randy Kobes, Stas]
+
=item 1.07 - Decemeber 22, 2003
allow conditional C module compilation on a more granular level.
1.16 +19 -1 httpd-test/perl-framework/Apache-Test/Makefile.PL
Index: Makefile.PL
===================================================================
RCS file: /home/cvs/httpd-test/perl-framework/Apache-Test/Makefile.PL,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -u -r1.15 -r1.16
--- Makefile.PL 6 Jan 2004 19:17:59 -0000 1.15
+++ Makefile.PL 7 Jan 2004 17:51:21 -0000 1.16
@@ -12,12 +12,14 @@
use ExtUtils::MakeMaker;
use Symbol;
+use File::Spec::Functions qw(catfile);
+use FindBin;
use Apache::TestMM qw(test); #enable 'make test'
+use Apache::TestRun;
my $VERSION;
-
Apache::TestMM::filter_args();
my @scripts = qw(t/TEST);
@@ -35,6 +37,8 @@
add_Apache__test_target();
}
+write_custom_config_file_stub();
+
WriteMakefile(
NAME => 'Apache::Test',
VERSION => $VERSION,
@@ -99,3 +103,17 @@
$string;
};
}
+
+# write the custom test file so it'll be copied to blib if updated
+# during 'make test' and then installed system-wide
+sub write_custom_config_file_stub {
+ # It doesn't matter whether it gets written under modperl-2.0/lib
+ # or Apache-Test/lib root, since Apache::TestRun uses the same
+ # logic and will update that file with real config data, which
+ # 'make install' will then pick and install system-wide
+ my $path = catfile $FindBin::Bin, "lib",
+ Apache::TestRun::CUSTOM_CONFIG_FILE;
+ # write an empty stub
+ Apache::TestRun::custom_config_write($path, '');
+}
+
1.193 +4 -15
httpd-test/perl-framework/Apache-Test/lib/Apache/TestConfig.pm
Index: TestConfig.pm
===================================================================
RCS file:
/home/cvs/httpd-test/perl-framework/Apache-Test/lib/Apache/TestConfig.pm,v
retrieving revision 1.192
retrieving revision 1.193
diff -u -u -r1.192 -r1.193
--- TestConfig.pm 5 Jan 2004 23:40:53 -0000 1.192
+++ TestConfig.pm 7 Jan 2004 17:51:21 -0000 1.193
@@ -17,6 +17,9 @@
use constant IS_MOD_PERL_2_BUILD => IS_MOD_PERL_2 &&
require Apache::Build && Apache::Build::IS_MOD_PERL_BUILD();
+use constant IS_APACHE_TEST_BUILD =>
+ grep { -e "$_/lib/Apache/TestConfig.pm" } qw(. ..);
+
use Symbol ();
use File::Copy ();
use File::Find qw(finddepth);
@@ -305,20 +308,6 @@
$vars->{target} ||= (WIN32 ? 'Apache.exe' : 'httpd');
- unless ($vars->{httpd}) {
- #sbindir should be bin/ with the default layout
- #but its eaiser to workaround apxs than fix apxs
- for my $dir (map { $vars->{$_} } qw(sbindir bindir)) {
- next unless defined $dir;
- my $httpd = catfile $dir, $vars->{target};
- next unless -x $httpd;
- $vars->{httpd} = $httpd;
- last;
- }
-
- $vars->{httpd} ||= $self->default_httpd;
- }
-
if ($vars->{httpd}) {
my @chunks = splitdir $vars->{httpd};
#handle both $prefix/bin/httpd and $prefix/Apache.exe
@@ -1366,7 +1355,7 @@
}
}
- my $exe = $vars->{apxs} || $vars->{httpd};
+ my $exe = $vars->{apxs} || $vars->{httpd} || '';
# if httpd.conf is older than executable
push @reasons,
"$exe is newer than $vars->{t_conf_file}"
1.132 +418 -14
httpd-test/perl-framework/Apache-Test/lib/Apache/TestRun.pm
Index: TestRun.pm
===================================================================
RCS file:
/home/cvs/httpd-test/perl-framework/Apache-Test/lib/Apache/TestRun.pm,v
retrieving revision 1.131
retrieving revision 1.132
diff -u -u -r1.131 -r1.132
--- TestRun.pm 19 Dec 2003 09:16:14 -0000 1.131
+++ TestRun.pm 7 Jan 2004 17:51:21 -0000 1.132
@@ -11,20 +11,29 @@
use Apache::TestHarness ();
use Apache::TestTrace;
+use Cwd;
use File::Find qw(finddepth);
-use File::Spec::Functions qw(catfile);
+use File::Spec::Functions qw(catfile catdir);
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 constant CUSTOM_CONFIG_FILE => 'Apache/TestConfigData.pm';
+
use subs qw(exit_shell exit_perl);
+my $original_command;
+my $orig_cwd;
+
my %core_files = ();
my %original_t_perms = ();
my @std_run = qw(start-httpd run-tests stop-httpd);
-my @others = qw(verbose configure clean help ssl http11 bugreport);
+my @others = qw(verbose configure clean help ssl http11 bugreport
save);
my @flag_opts = (@std_run, @others);
my @string_opts = qw(order trace);
my @ostring_opts = qw(proxy ping);
@@ -57,9 +66,24 @@
'ssl' => 'run tests through ssl',
'proxy' => 'proxy requests (default proxy is localhost)',
'trace=T' => 'change tracing default to: warning, notice, info,
debug, ...',
+ 'save' => 'save test paramaters into Apache::TestConfigData',
(map { $_, "\U$_\E url" } @request_opts),
);
+# variables stored in $Apache::TestConfigData::vars
+my @data_vars_must = qw(httpd apxs);
+my @data_vars_opt = qw(user group port);
+# mapping from $Apache::TestConfigData::vars to $ENV settings
+my %vars_to_env = (
+ httpd => 'APACHE',
+ user => 'APACHE_USER',
+ group => 'APACHE_GROUP',
+ apxs => 'APXS',
+ port => 'APACHE_PORT',
+);
+
+custom_config_load();
+
sub fixup {
#make sure we use an absolute path to perl
#else Test::Harness uses the perl in our PATH
@@ -435,6 +459,19 @@
$test_config->cmodules_configure;
$test_config->generate_httpd_conf;
$test_config->save;
+
+ # save if
+ # 1) requested to save
+ # 2) no saved config yet
+ # 3) A-T build (since it'll override the global file)
+ # the only side-effect of (3) is that if config is changed during
+ # A-T build it'll change the global custom config if that exists,
+ # but it shouldn't affect normal users who won't do it more than once
+ if ($self->{opts}->{save}
+ or !$self->custom_config_exists()
+ or IS_APACHE_TEST_BUILD) {
+ $self->custom_config_save();
+ }
}
sub try_exit_opts {
@@ -470,12 +507,17 @@
my $test_config = $self->{test_config};
unless ($test_config->{vars}->{httpd}) {
- error "no test server configured, please specify an httpd or ".
- ($test_config->{APXS} ?
- "an apxs other than $test_config->{APXS}" : "apxs").
- " or put either in your PATH. For example:\n" .
- "$0 -httpd /path/to/bin/httpd";
- exit_perl 0;
+ $self->opt_clean(1);
+ # this method restarts the whole program via exec
+ # so it never returns
+ $self->custom_config_first_time;
+ }
+
+ # if we have gotten that far we know at least about the location
+ # of httpd, so let's save it if we haven't saved any custom
+ # configs yet
+ unless ($self->custom_config_exists()) {
+ $self->custom_config_save();
}
my $opts = $self->{opts};
@@ -541,6 +583,10 @@
sub new_test_config {
my $self = shift;
+ for (@data_vars_must, @data_vars_opt) {
+ next unless $Apache::TestConfigData::vars->{$_};
+ $self->{conf_opts}->{$_} ||= $Apache::TestConfigData::vars->{$_};
+ }
Apache::TestConfig->new($self->{conf_opts});
}
@@ -563,12 +609,10 @@
}
close $sh;
- # reconstruct argv, preserve multiwords args, eg 'PerlTrace all'
- my $argv = join " ", map { /^-/ ? $_ : qq['$_'] } @ARGV;
- my $command = "ulimit -c unlimited; $0 $argv";
- warning "setting ulimit to allow core files\n$command";
- exec $command;
- die "exec $command has failed"; # shouldn't be reached
+ $original_command = "ulimit -c unlimited; $original_command";
+ warning "setting ulimit to allow core files\n$original_command";
+ exec $original_command;
+ die "exec $original_command has failed"; # shouldn't be reached
}
sub set_ulimit {
@@ -587,6 +631,10 @@
sub run {
my $self = shift;
+ # reconstruct argv, preserve multiwords args, eg 'PerlTrace all'
+ my $argv = join " ", map { /^-/ ? $_ : qq['$_'] } @ARGV;
+ $original_command = "$0 $argv";
+ $orig_cwd = Cwd::cwd();
$self->set_ulimit;
$self->set_env; #make sure these are always set
@@ -1087,6 +1135,324 @@
CORE::exit $_[0];
}
+# determine where the configuration file Apache/TestConfigData.pm
+# lives. The order searched is:
+# 1) $ENV{HOME}/.apache-test/
+# 2) in global @INC (if preconfigured already)
+# 3) in local lib (for build times)
+my $custom_config_path;
+sub custom_config_path {
+
+ return $custom_config_path if $custom_config_path;
+
+ my @global_inc = ();
+ my @build_inc = ();
+ for (@INC) {
+ my $rel_dir = basename $_;
+ $rel_dir eq 'lib' ? push(@build_inc, $_) : push(@global_inc, $_);
+ }
+
+ # XXX $ENV{HOME} isn't propagated in mod_perl
+ unshift @global_inc, catdir $ENV{HOME}, '.apache-test' if $ENV{HOME};
+
+ for (@global_inc, @build_inc) {
+ my $candidate = File::Spec->rel2abs(catfile $_, CUSTOM_CONFIG_FILE);
+ next unless -e $candidate;
+ return $custom_config_path = $candidate;
+ }
+
+ return '';
+}
+
+sub custom_config_exists {
+ my $self = shift;
+ # custom config gets loaded via custom_config_load when this
+ # package is loaded. it's enough to check whether we have a custom
+ # config for 'httpd'.
+ my $httpd = $Apache::TestConfigData::vars->{httpd} || '';
+ return $httpd && -e $httpd ? 1 : 0;
+}
+
+sub custom_config_save {
+ my $self = shift;
+ my $vars = $self->{test_config}->{vars};
+ my $conf_opts = $self->{conf_opts};
+ my $config_dump = '';
+
+ # minimum httpd needs to be set
+ return 0 unless $vars->{httpd} or $Apache::TestConfigData::vars->{httpd};
+
+ # it doesn't matter how these vars were set (apxs may get set
+ # using the path to httpd, or the other way around)
+ for (@data_vars_must) {
+ next unless my $var = $vars->{$_} || $conf_opts->{$_};
+ $config_dump .= qq{ '$_' => '$var',\n};
+ }
+
+ # save these vars only if they were explicitly set via command line
+ # options. For example if someone builds A-T as user 'foo', then
+ # installs it as root and we save it, all users will now try to
+ # configure under that user 'foo' which won't quite work.
+ for (@data_vars_opt) {
+ next unless my $var = $conf_opts->{$_};
+ $config_dump .= qq{ '$_' => '$var',\n};
+ }
+
+ my $path;
+ # if A-T build always update the build file, regardless whether
+ # the global file already exists, since 'make install' will
+ # overwrite it
+ if (IS_APACHE_TEST_BUILD) {
+ $path = catfile $vars->{top_dir}, 'lib', CUSTOM_CONFIG_FILE;
+ }
+ elsif ($path = custom_config_path() ) {
+ # do nothing, the config file already exists (global)
+ debug "Found custom config '$path'";
+ }
+ else {
+ # next try a global location, as if it was configured before
+ # Apache::Test's 'make install'
+ my $base = dirname dirname $INC{"Apache/TestRun.pm"};
+ $path = catdir $base, CUSTOM_CONFIG_FILE;
+ }
+
+ # check whether we can write to the directory of the chosen path
+ if ($path) {
+ # first make sure that the file is writable if it exists
+ # already (it might be non-writable if installed via EU::MM)
+ if (-e $path) {
+ my $mode = (stat _)[2];
+ $mode |= 0200;
+ chmod $mode, $path; # it's ok if we fail
+ }
+
+ # try to open for append
+ my $fh = Symbol::gensym;
+ if (open $fh, ">>$path") {
+ close $fh;
+ }
+ else {
+ $path = ''; # can't use that path to write custom config
+ }
+ }
+
+ # if we have no path yet, try to use ~
+ if (!$path and $ENV{HOME}) {
+ $path = catfile $ENV{HOME}, '.apache-test', CUSTOM_CONFIG_FILE;
+ }
+
+ if ($path) {
+ custom_config_write($path, $config_dump);
+ return 1;
+ }
+
+ return 0; # failed to write config XXX: should we croak?
+}
+
+sub custom_config_write {
+ my($path, $config_dump) = @_;
+
+ my $pkg = << "EOC";
+package Apache::TestConfigData;
+
+use strict;
+use warnings;
+
+\$Apache::TestConfigData::vars = {
+$config_dump
+};
+
+1;
+
+=head1 NAME
+
+Apache::TestConfigData - Configuration file for Apache::Test
+
+=cut
+EOC
+
+ debug "Writing custom config $path";
+ require File::Path;
+ my $dir = dirname $path;
+ File::Path::mkpath($dir, 0, 0755) unless -e $dir;
+ my $fh = Symbol::gensym;
+ open $fh, ">$path" or die "Cannot open $path: $!";
+ print $fh $pkg;
+ close $fh;
+}
+
+sub custom_config_load {
+ debug "trying to load custom config data";
+
+ if (my $custom_config_path = custom_config_path()) {
+ debug "loading custom config path '$custom_config_path'";
+
+ require $custom_config_path;
+
+ # %ENV overloads any custom saved config (command-line option will
+ # override any of these settings), though only if assigned
+ # some value (this is because Makefile will pass empty env
+ # vars if none were assigned)
+ for (@data_vars_must, @data_vars_opt) {
+ my $value = $ENV{ $vars_to_env{$_} };
+ next unless defined $value and length $value;
+ $Apache::TestConfigData::vars->{$_} = $value;
+ }
+ }
+}
+
+sub custom_config_first_time {
+ my $self = shift;
+
+ my $test_config = $self->{test_config};
+ my $vars = $test_config->{vars};
+
+ require ExtUtils::MakeMaker;
+ require File::Spec;
+ local *which = \&Apache::TestConfig::which;
+
+ print qq[
+
+We are now going to configure the Apache-Test framework.
+This configuration process needs to be done only once.
+
+];
+
+ print qq[
+
+First we need to know where the 'httpd' executable is located.
+If you have more than one Apache server is installed, make sure
+you supply the path to the one you are going to use for testing.
+You can always override this setting at run time via the '-httpd'
+option. For example:
+
+ % t/TEST -httpd /path/to/alternative/httpd
+
+or via the environment variable APACHE. For example:
+
+ % APACHE=/path/to/alternative/httpd t/TEST
+
+];
+
+ {
+ my %choices = ();
+ for (grep defined $_,
+ map({ catfile $vars->{$_}, $vars->{target} } qw(sbindir
bindir)),
+ $test_config->default_httpd, which($vars->{target}),
+ $ENV{APACHE}, which('apache'), which('httpd'),
+ $ENV{APACHE2}, which('apache2'), which('httpd2')) {
+ $choices{$_}++ if -e $_ && -x _;
+ }
+ my $optional = 0;
+ my $wanted = 'httpd';
+ $test_config->{vars}->{$wanted} =
+ _custom_config_prompt_path($wanted, \%choices, $optional);
+ }
+
+ print qq[
+
+Next we need to know where the 'apxs' script is located. This script
+provides a lot of information about the apache installation, and makes
+it easier to find things. However it's not available on all platforms,
+therefore it's optional. If you don't have it installed it's not a
+problem. Notice that if you have Apache 2.x installed that script
+could be called as 'apxs2'.
+
+If you have more than one Apache server is installed, make sure you
+supply the path to the apxs script you are going to use for testing.
+You can always override this setting at run time via the '-apxs'
+option. For example:
+
+ % t/TEST -apxs /path/to/alternative/apxs
+
+or via the environment variable APACHE. For example:
+
+ % APXS=/path/to/alternative/apxs t/TEST
+
+];
+ {
+ my %choices = ();
+ for (grep defined $_,
+ map({ catfile $vars->{$_}, 'apxs' } qw(sbindir bindir)),
+ $test_config->default_apxs,
+ $ENV{APXS}, which('apxs'),
+ $ENV{APXS2}, which('apxs2')) {
+ $choices{$_}++ if -e $_ && -x _;
+ }
+ my $optional = 1;
+ my $wanted = 'apxs';
+ $test_config->{vars}->{$wanted} =
+ _custom_config_prompt_path($wanted, \%choices, $optional);
+ }
+
+ $self->custom_config_save();
+
+ # we probably could reconfigure on the fly ($self->configure), but
+ # the problem is various cached data which won't be refreshed. so
+ # the simplest is just to restart the run from scratch
+ chdir $orig_cwd;
+ warning "rerunning '$original_command' with new config opts";
+ exec $original_command;
+ die "exec $original_command has failed"; # shouldn't be reached
+}
+
+sub _custom_config_prompt_path {
+ my($wanted, $rh_choices, $optional) = @_;
+
+ my $ans;
+ my $default = '';
+ my $optional_str = $optional ? " (optional)" : '';
+ my $prompt =
+ "\nPlease provide a full path to$optional_str '$wanted' executable";
+
+ my @choices = ();
+ if (%$rh_choices) {
+ $prompt .= " or choose from the following options:\n\n";
+ my $c = 0;
+ for (sort keys %$rh_choices) {
+ $c++;
+ $prompt .= " [$c] $_\n";
+ push @choices, $_;
+ }
+ $prompt .= " \n";
+ $default = 1; # a wild guess
+ }
+ else {
+ $prompt .= ":\n\n";
+ }
+
+ while (1) {
+ $ans = ExtUtils::MakeMaker::prompt($prompt, $default);
+
+ # convert the item number to the path
+ if ($ans =~ /^\s*(\d+)\s*$/) {
+ if ($1 > 0 and $choices[$1-1]) {
+ $ans = $choices[$1-1];
+ }
+ else {
+ warn "The choice '$ans' doesn't exist\n";
+ next;
+ }
+ }
+
+ if ($optional) {
+ return '' unless $ans;
+ }
+
+ unless (File::Spec->file_name_is_absolute($ans)) {
+ my $cwd = Cwd::cwd();
+ warn "The path '$ans' is not an absolute path. " .
+ "Please specify an absolute path\n";
+ next;
+ }
+
+ warn("'$ans' doesn't exist\n"), next unless -e $ans;
+ warn("'$ans' is not executable\n"), next unless -x $ans;
+
+ return $ans;
+ }
+}
+
1;
__END__
@@ -1162,6 +1528,44 @@
}
Notice that the extension is I<.c>, and not I<.so>.
+
+=head1 Saving options
+
+When C<Apache::Test> is first installed, it will save the
+values of C<httpd>, C<port>, C<apxs>, C<user>, and C<group>,
+if set, to a configuration file C<Apache::TestConfigData>.
+This information will then be used in setting these options
+for subsequent uses.
+
+The values stored in C<Apache::TestConfigData> can be overriden
+temporarily either by setting the appropriate environment
+variable or by giving the relevant option when the C<TEST>
+script is run. If you want to save these options to
+C<Apache::TestConfigData>, use the C<-save> flag when
+running C<TEST>.
+
+If you are running C<Apache::Test> as a
+user who does not have permission to alter the system
+C<Apache::TestConfigData>, you can place your
+own private configuration file F<TestConfigData.pm>
+under C<$ENV{HOME}/.apache-test/Apache/>,
+which C<Apache::Test> will use, if present. An example
+of such a configuration file is
+
+ # file $ENV{HOME}/.apache-test/Apache/TestConfigData.pm
+ package Apache::TestConfigData;
+ use strict;
+ use warnings;
+ use vars qw($vars);
+
+ $vars = {
+ 'group' => 'me',
+ 'user' => 'myself',
+ 'port' => '8529',
+ 'httpd' => '/usr/local/apache/bin/httpd',
+
+ };
+ 1;
=head2 C<new_test_config>