You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl-cvs@perl.apache.org by do...@apache.org on 2001/04/02 10:58:08 UTC

cvs commit: modperl-2.0/Apache-Test/lib/Apache TestConfig.pm

dougm       01/04/02 01:58:08

  Added:       Apache-Test/lib/Apache TestConfig.pm
  Log:
  base test config generator
  
  Revision  Changes    Path
  1.1                  modperl-2.0/Apache-Test/lib/Apache/TestConfig.pm
  
  Index: TestConfig.pm
  ===================================================================
  package Apache::TestConfig;
  
  use strict;
  use warnings FATAL => 'all';
  
  use constant WIN32 => $^O eq 'MSWin32';
  
  use File::Spec::Functions qw(catfile abs2rel splitdir);
  use Cwd qw(fastcwd);
  
  use Apache::TestConfigPerl ();
  use Apache::TestConfigParse ();
  
  use Apache::TestServer ();
  
  my %usage = (
     top_dir       => 'top-level directory (default is $PWD)',
     t_dir         => 'the t/ test directory (default is $top_dir/t)',
     t_conf        => 'the conf/ test directory (default is $t_dir/conf)',
     t_logs        => 'the logs/ test directory (default is $t_dir/logs)',
     t_conf_file   => 'test httpd.conf file (default is $t_conf/httpd.conf)',
     src_dir       => 'source directory to look for mod_foos.so',
     serverroot    => 'ServerRoot (default is $t_dir)',
     documentroot  => 'DocumentRoot (default is $ServerRoot/htdocs',
     port          => 'Port (default is 8529)',
     servername    => 'ServerName (default is localhost)',
     user          => 'User to run test server as (default is $USER)',
     group         => 'Group to run test server as (default is $GROUP)',
     bindir        => 'Apache bin/ dir (default is apxs -q SBINDIR)',
     httpd         => 'server to use for testing (default is $bindir/httpd)',
     target        => 'name of server binary (default is apxs -q TARGET)',
     apxs          => 'location of apxs (default is from Apache::BuildConfig)',
     httpd_conf    => 'inherit config from this file (default is apxs derived)',
  );
  
  sub usage {
      for my $hash (\%usage) {
          while (my($key, $val) = each %$hash) {
              printf "   %-16s %s\n", $key, $val;
          }
      }
  }
  
  my %passenv = map { $_,1 } qw{
  APXS APACHE APACHE_GROUP APACHE_USER APACHE_PORT
  };
  
  sub passenv {
      \%passenv;
  }
  
  sub server { shift->{server} }
  
  sub build_config {
      eval {
          require Apache::BuildConfig;
      } or return undef;
      return Apache::Build->build_config;
  }
  
  sub new_test_server {
      my($self, $args) = @_;
      Apache::TestServer->new($args || $self)
  }
  
  sub new {
      my($class, $args) = @_;
  
      $args = ($args and ref($args)) ? {%$args} : {@_}; #copy
  
      my $thaw = {};
  
      #thaw current config
      for (qw(conf t/conf)) {
          last if eval {
              require "$_/apache_test_config.pm";
              $thaw = 'apache_test_config'->new;
              delete $thaw->{save};
          };
      };
  
      if ($args->{thaw}) {
          #dont generate any new config
          $thaw->{$_} = $args->{$_} for keys %$args;
          $thaw->{server} = $thaw->new_test_server;
          return $thaw;
      }
  
      #regenerating config, so forget old
      if ($args->{save}) {
          for (qw(vhosts inherit_config modules inc)) {
              delete $thaw->{$_} if exists $thaw->{$_};
          }
      }
  
      my $self = bless {
          clean => {},
          vhosts => {},
          inherit_config => {},
          modules => {},
          inc => [],
          %$thaw,
          vars => $args,
          postamble => [],
          preamble => [],
          postamble_hooks => [],
          preamble_hooks => [],
      }, $class;
  
      my $vars = $self->{vars}; #things that can be overridden
  
      for (qw(save verbose)) {
          next unless exists $args->{$_};
          $self->{$_} = delete $args->{$_};
      }
  
      $vars->{top_dir} ||= fastcwd;
      $vars->{top_dir} = pop_dir($vars->{top_dir}, 't');
  
      $self->add_inc;
  
      #help to find libmodperl.so
      my $src_dir = catfile $vars->{top_dir}, qw(src modules perl);
      $vars->{src_dir}      ||= $src_dir if -d $src_dir;
  
      $vars->{t_dir}        ||= catfile $vars->{top_dir}, 't';
      $vars->{serverroot}   ||= $vars->{t_dir};
      $vars->{documentroot} ||= catfile $vars->{serverroot}, 'htdocs';
      $vars->{t_conf}       ||= catfile $vars->{serverroot}, 'conf';
      $vars->{t_logs}       ||= catfile $vars->{serverroot}, 'logs';
      $vars->{t_conf_file}  ||= catfile $vars->{t_conf},   'httpd.conf';
  
      $vars->{port}         ||= $self->default_port;
      $vars->{servername}   ||= $self->default_servername;
      $vars->{user}         ||= $self->default_user;
      $vars->{group}        ||= $self->default_group;
      $vars->{serveradmin}  ||= join '@', $vars->{user}, $vars->{servername};
  
      $self->configure_apxs;
      $self->configure_httpd;
      $self->inherit_config; #see TestConfigParse.pm
  
      $self->{hostport} = $self->hostport;
  
      $self->{server} = $self->new_test_server;
  
      $self;
  }
  
  sub configure_apxs {
      my $self = shift;
  
      return unless $self->{MP_APXS} = $self->default_apxs;
      my $vars = $self->{vars};
  
      $vars->{bindir}   ||= $self->apxs('SBINDIR');
      $vars->{target}   ||= $self->apxs('TARGET');
      $vars->{conf_dir} ||= $self->apxs('SYSCONFDIR');
  
      if ($vars->{conf_dir}) {
          $vars->{httpd_conf} ||= catfile $vars->{conf_dir}, 'httpd.conf';
      }
  }
  
  sub configure_httpd {
      my $self = shift;
      my $vars = $self->{vars};
  
      $vars->{target} ||= 'httpd';
  
      if ($vars->{bindir}) {
          $vars->{httpd} ||= catfile $vars->{bindir}, $vars->{target};
      }
      else {
          $vars->{httpd} ||= $self->default_httpd;
      }
  
      if ($vars->{httpd}) {
          my @chunks = splitdir $vars->{httpd};
          pop @chunks for 1..2; #bin/httpd
          $self->{httpd_basedir} = catfile @chunks;
      }
  
      #cleanup httpd droppings
      my $sem = catfile $vars->{t_logs}, 'apache_runtime_status.sem';
      unless (-e $sem) {
          $self->{clean}->{files}->{$sem} = 1;
      }
  }
  
  sub add_config {
      my $self = shift;
      my $where = shift;
      my($directive, $arg, $hash) = @_;
      my $args = "";
  
      if ($hash) {
          $args = "<$directive $arg>\n";
          if (ref($hash)) {
              while (my($k,$v) = each %$hash) {
                  $args .= "   $k $v\n";
              }
          }
          else {
              $args .= "   $hash";
          }
          $args .= "</$directive>";
      }
      elsif (ref($directive) eq 'ARRAY') {
          $args = join "\n", @$directive;
      }
      else {
          $args = "$directive " .
            (ref($arg) && (ref($arg) eq 'ARRAY') ? "@$arg" : $arg);
      }
  
      push @{ $self->{$where} }, $args;
  }
  
  sub postamble {
      shift->add_config(postamble => @_);
  }
  
  sub preamble {
      shift->add_config(preamble => @_);
  }
  
  sub postamble_register {
      push @{ shift->{postamble_hooks} }, @_;
  }
  
  sub preamble_register {
      push @{ shift->{preamble_hooks} }, @_;
  }
  
  sub add_config_hooks_run {
      my($self, $where, $out) = @_;
  
      for (@{ $self->{"${where}_hooks"} }) {
          $self->$_();
      }
  
      for (@{ $self->{$where} }) {
          $self->replace;
          print $out "$_\n";
      }
  }
  
  sub postamble_run {
      shift->add_config_hooks_run(postamble => @_);
  }
  
  sub preamble_run {
      shift->add_config_hooks_run(preamble => @_);
  }
  
  sub default_group {
      my $gid = $);
  
      #use only first value if $) contains more than one
      $gid =~ s/^(\d+).*$/$1/;
  
      WIN32 ? 'nogroup' :
          $ENV{APACHE_GROUP} || (getgrgid($gid) || "#$gid");
  }
  
  sub default_user {
      my $uid = $>;
  
      my $user = WIN32 ? 'nobody' :
        $ENV{APACHE_USER} || (getpwuid($uid) || "#$uid");
  
      if ($user eq 'root') {
  	my $other = (getpwnam('nobody'))[0];
          if ($other) {
              $user = $other;
          }
          else {
              die "cannot run tests as User root";
              #XXX: prompt for another username
          }
      }
  
      $user;
  }
  
  sub default_apxs {
      my $self = shift;
  
      return $self->{vars}->{apxs} if $self->{vars}->{apxs};
  
      if (my $build_config = build_config()) {
          return $build_config->{MP_APXS};
      }
  
      $ENV{APXS} || which('apxs');
  }
  
  sub default_httpd {
      my $vars = shift->{vars};
  
      $ENV{APACHE} || which($vars->{target});
  }
  
  sub default_servername {
      'localhost';
  }
  
  #XXX: could check if the port is in use and select another if so
  sub default_port {
      $ENV{APACHE_PORT} || 8529;
  }
  
  sub default_loopback {
      '127.0.0.1';
  }
  
  sub port {
      my($self, $module) = @_;
      return $self->{vars}->{port} unless $module;
      return $self->{vhosts}->{$module}->{port};
  }
  
  sub hostport {
      my $self = shift;
      my $vars = shift || $self->{vars};
  
      my $name = $vars->{servername};
      my $resolve = \$self->{resolved}->{$name};
  
      unless ($$resolve) {
          if (gethostbyname $name) {
              $$resolve = $name;
          }
          else {
              $$resolve = $self->default_loopback;
              warn "lookup $name failed, using $$resolve for client tests\n";
          }
      }
  
      join ':', $$resolve, $vars->{port};
  }
  
  #look for mod_foo.so
  sub find_apache_module {
      my($self, $module) = @_;
  
      my $vars = $self->{vars};
      my $sroot = $vars->{serverroot};
  
      my @trys = grep { $_ }
        ($vars->{src_dir},
         $self->apxs('LIBEXECDIR'),
         catfile($sroot, 'modules'),
         catfile($sroot, 'libexec'));
  
      for (@trys) {
          my $file = catfile $_, $module;
          if (-e $file) {
              $self->trace("found $module => $file");
              return $file;
          }
      }
  }
  
  #generate files and directories
  sub genfile {
      my($self, $file) = @_;
  
      my $name = abs2rel $file, $self->{vars}->{t_dir};
      $self->trace("generating $name");
  
      open my $fh, '>', $file or die "open $file: $!";
      $self->{clean}->{files}->{$file} = 1;
  
      return $fh;
  }
  
  sub gendir {
      my($self, $dir) = @_;
  
      mkdir $dir, 0755 unless -d $dir;
      $self->{clean}->{dirs}->{$dir} = 1;
  }
  
  sub clean {
      my $self = shift;
  
      for (keys %{ $self->{clean}->{files} }) {
          if (-e $_) {
              $self->trace("unlink $_");
              unlink $_;
          }
          else {
              #print "unlink $_: $!\n";
          }
      }
  
      for (keys %{ $self->{clean}->{dirs} }) {
          if (-d $_) {
              opendir(my $dh, $_);
              my $notempty = grep { ! /^\.{1,2}$/ } readdir $dh;
              closedir $dh;
              next if $notempty;
              $self->trace("rmdir $_");
              rmdir $_;
          }
      }
  
      $self->new_test_server->clean;
  }
  
  sub replace {
      my $self = shift;
      s/@(\w+)@/$self->{vars}->{lc $1}/g;
  }
  
  sub replace_vars {
      my($self, $in, $out) = @_;
  
      local $_;
      while (<$in>) {
          $self->replace;
          print $out $_;
      }
  }
  
  sub index_html_template {
      my $self = shift;
      return "welcome to $self->{server}->{name}\n";
  }
  
  sub generate_index_html {
      my $self = shift;
      my $dir = $self->{vars}->{documentroot};
      $self->gendir($dir);
      my $file = catfile $dir, 'index.html';
      return if -e $file;
      my $fh = $self->genfile($file);
      print $fh $self->index_html_template;
  }
  
  sub types_config_template {
      "text/html html htm\n";
  }
  
  sub generate_types_config {
      my $self = shift;
  
      unless ($self->{inherit_config}->{TypesConfig}) {
          my $types = catfile $self->{vars}->{t_conf}, 'mime.types';
          unless (-e $types) {
              my $fh = $self->genfile($types);
              print $fh $self->types_config_template;
              close $fh;
          }
          $self->postamble(TypesConfig => qq("$types"));
      }
  }
  
  sub httpd_conf_template {
      my($self, $try) = @_;
  
      if (open my $in, $try) {
          return $in;
      }
      else {
          return \*DATA;
      }
  }
  
  sub generate_extra_conf {
      my $self = shift;
  
      my $extra_conf = catfile $self->{vars}->{t_conf}, 'extra.conf';
      return $extra_conf if -e $extra_conf;
  
      my $extra_conf_in = join '.', $extra_conf, 'in';
      open(my $in, $extra_conf_in) or return;
  
      my $out = $self->genfile($extra_conf);
      $self->replace_vars($in, $out);
  
      close $in;
      close $out;
  
      return $extra_conf;
  }
  
  sub generate_httpd_conf {
      my $self = shift;
  
      #generated httpd.conf depends on these things to exist
      $self->generate_types_config;
      $self->generate_index_html;
  
      for (qw(t_logs t_conf)) {
          $self->gendir($self->{vars}->{$_});
      }
  
      if (my $extra_conf = $self->generate_extra_conf) {
          $self->postamble(Include => qq("$extra_conf"));
      }
  
      my $conf_file = $self->{vars}->{t_conf_file};
      my $conf_file_in = join '.', $conf_file, 'in';
  
      my $in = $self->httpd_conf_template($conf_file_in);
  
      my $out = $self->genfile($conf_file);
  
      $self->preamble_run($out);
  
      $self->replace_vars($in, $out);
  
      print $out "\n";
  
      $self->postamble_run($out);
  
      close $in;
      close $out or die "close $conf_file: $!";
  }
  
  #shortcuts
  
  my %include_headers = (GET => 1, HEAD => 2);
  
  sub http_raw_get {
      my($self, $url, $h) = @_;
  
      $url = "/$url" unless $url =~ m:^/:;
  
      my $ih = exists $include_headers{$h ||= 0} ?
        $include_headers{$h} : $h;
  
      require Apache::TestRequest;
      Apache::TestRequest::http_raw_get($self->{hostport},
                                        $url, $ih);
  }
  
  sub error_log {
      my($self, $rel) = @_;
      my $file = catfile $self->{vars}->{t_logs}, 'error_log';
      return $file unless $rel;
      return abs2rel $file, $self->{vars}->{top_dir};
  }
  
  
  #utils
  
  sub trace {
      my $self = shift;
      return unless $self->{verbose};
      print "@_\n";
  }
  
  #duplicating small bits of Apache::Build so we dont require it
  sub which {
      foreach (map { catfile $_, $_[0] } File::Spec->path) {
  	return $_ if -x;
      }
  }
  
  sub apxs {
      my($self, $q) = @_;
      return unless $self->{MP_APXS};
      my $val = qx($self->{MP_APXS} -q $q 2>/dev/null);
      warn "APXS ($self->{MP_APXS}) query for $q failed\n" unless $val;
      $val;
  }
  
  sub pop_dir {
      my $dir = shift;
  
      my @chunks = splitdir $dir;
      while (my $remove = shift) {
          pop @chunks if $chunks[-1] eq $remove;
      }
  
      catfile @chunks;
  }
  
  sub add_inc {
      my $self = shift;
      require lib;
      lib::->import(map "$self->{vars}->{top_dir}/$_",
                    qw(lib blib/lib blib/arch));
      #print join "\n", @INC, "";
  }
  
  #freeze/thaw so other processes can access config
  
  sub thaw {
      my $class = shift;
      $class->new({thaw => 1, @_});
  }
  
  sub freeze {
      require Data::Dumper;
      local $Data::Dumper::Terse = 1;
      my $data = Data::Dumper::Dumper(shift);
      chomp $data;
      $data;
  }
  
  sub save {
      my($self) = @_;
  
      return unless $self->{save};
  
      my $name = 'apache_test_config';
      my $file = catfile $self->{vars}->{t_conf}, "$name.pm";
      my $fh = $self->genfile($file);
  
      $self->trace("saving config data to $name.pm");
  
      (my $obj = $self->freeze) =~ s/^/    /;
  
      print $fh <<EOF;
  package $name;
  
  sub new {
  $obj;
  }
  
  1;
  EOF
  
      close $fh or die "failed to write $file: $!";
  }
  
  1;
  __DATA__
  ServerRoot   "@ServerRoot@"
  DocumentRoot "@DocumentRoot@"
  
  Listen     @Port@
  Group      @Group@
  User       @User@
  ServerName @ServerName@
  
  PidFile     @t_logs@/httpd.pid
  ErrorLog    @t_logs@/error_log
  LogLevel    debug
  TransferLog @t_logs@/access_log
  
  ServerAdmin @ServerAdmin@
  
  KeepAlive       Off
  HostnameLookups Off
  
  <Directory />
      Options FollowSymLinks
      AllowOverride None
  </Directory>
  
  <IfModule threaded.c>
      StartServers         1
      MaxClients           1
      MinSpareThreads      1
      MaxSpareThreads      1
      ThreadsPerChild      1
      MaxRequestsPerChild  0
  </IfModule>
  
  <IfModule prefork.c>
      StartServers         1
      MaxClients           1
      MaxRequestsPerChild  0
  </IfModule>
  
  <Location /server-info>
      SetHandler server-info
  </Location>
  
  <Location /server-status>
      SetHandler server-status
  </Location>