# Copyright 2001-2006 The Apache Software Foundation or its licensors, as
# applicable.
#
# 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.
#
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';
    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
;
; 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 = On
asp_tags = Off
precision    =  14
y2k_compliance = On
;output_buffering = 4096
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
max_input_time = 60
memory_limit = 8M
error_reporting  =  E_ALL
display_errors = Off
;display_startup_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
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 =
extension_dir = "./"
enable_dl = On
file_uploads = On
upload_max_filesize = 2M
allow_url_fopen = On
default_socket_timeout = 60
[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_password =
mysqli.reconnect = Off
[mSQL]
msql.allow_persistent = On
msql.max_persistent = -1
msql.max_links = -1
[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
[dbx]
dbx.colnames_case = "lowercase"
[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.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]
[Ingres II]
ingres.allow_persistent = On
ingres.max_persistent = -1
ingres.max_links = -1
ingres.default_database =
ingres.default_user =
ingres.default_password =
[Verisign Payflow Pro]
pfpro.defaulthost = "test-payflow.verisign.com"
pfpro.defaultport = 443
pfpro.defaulttimeout = 30
[Sockets]
sockets.use_system_read = On
[com]
[mbstring]
[FrontBase]
[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.");
    }
}

?>
