<?php

// Copyright (c) 2006 Roman Neuhauser
// 
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// $HeadURL: https://svn.sigpipe.cz/r/trunk/testilence/src/tence/main.php $
// $Id: main.php 414 2007-02-20 01:05:56Z roman $


require_once 'Testilence/reporter/iface.php';
require_once 
'tence/runner.php';

class 
tence_util_ClassList extends ArrayIterator # {{{
{
    function 
__construct($file# {{{
    
{
        
$loader = new Tence_BasicLoader;
        
$before get_declared_classes();
        
$loader->loadFile($file);
        
$after get_declared_classes();
        
parent::__construct(array_values(array_diff($after$before)));
    } 
# }}}
# }}}
class tence_util_ClassFilter # {{{
{
    private 
$names$rcs = array();
    public function 
__construct(array $names# {{{
    
{
        
$this->names $names;
        foreach (
$names as $cls) {
            
$rc = new ReflectionClass($cls);
            
array_push($this->rcs$rc->getName());
        }
    } 
# }}}
    
public function check(ReflectionClass $rc# {{{
    
{
        return
            
$this->isOfAnyClass($rc$this->names)
         || 
$this->implementsAnyInterface($rc$this->names)
        ;
    } 
# }}}
    
protected function implementsAnyInterface(ReflectionClass $rc, array $ifnames# {{{
    
{
        return 
array_intersect(
            
array_keys($rc->getInterfaces()),
            
$ifnames
        
);
    } 
# }}}
    
protected function isOfAnyClass(ReflectionClass $rc, array $clsnames# {{{
    
{
        
$name $rc->getName();
        foreach (
$clsnames as $cls) {
            if (
== strcasecmp($name$cls) || $rc->isSubclassOf($cls)) {
                return 
true;
            }
        }
        return 
false;
    } 
# }}}
# }}}
class tence_util_TestFilter # {{{
{
    public function 
__construct(array $testTypes = array('Tence_RunnableTest')) # {{{
    
{
        
$this->cf = new tence_util_ClassFilter($testTypes);
    } 
# }}}
    
public function isTest(ReflectionClass $rc# {{{
    
{
        return 
$this->hasDesiredType($rc)
            && 
$this->isEasilyInstantiable($rc)
        ;
    } 
# }}}
    
protected function hasDesiredType(ReflectionClass $rc# {{{
    
{
        return 
$this->cf->check($rc);
    } 
# }}}
    
protected function isEasilyInstantiable(ReflectionClass $rc# {{{
    
{
        return 
$rc->isInstantiable()
            && 
$this->hasNullaryConstructor($rc)
        ;
    } 
# }}}
    
protected function hasNullaryConstructor(ReflectionClass $rc# {{{
    
{
        
$con $rc->getConstructor();
        return 
is_null($con)
            || 
== $con->getNumberOfRequiredParameters()
        ;
    } 
# }}}
# }}}
class tence_util_TestFilterIterator extends FilterIterator # {{{
{
    private 
$filter;
    function 
__construct(Iterator $it# {{{
    
{
        
$this->filter = new tence_util_TestFilter;
        
parent::__construct($it);
    } 
# }}}
    
function accept() # {{{
    
{
        return 
$this->filter->isTest(
            new 
ReflectionClass($this->getInnerIterator()->current())
        );
    } 
# }}}
# }}}
class tence_TestList # {{{
implements IteratorAggregate
{
    function 
__construct($file# {{{
    
{
        
$this->file $file;
    } 
# }}}
    
function getIterator() # {{{
    
{
        return new 
tence_util_TestFilterIterator(
            new 
tence_util_ClassList($this->file)
        );
    } 
# }}}
# }}}
class tence_TestLister # {{{
{
    public function 
list_(tence_TestList $tests# {{{
    
{
        
$i 0;
        foreach (
$tests as $test) {
            ++
$i;
            
fprintf(STDOUT"%s\n"$test);
        }
        return 
$i;
    } 
# }}}
# }}}

interface tence_cmdline_exitValueComputer
{
    static function 
compute(Tence_Reporter $tr);
}

class 
tence_cmdline_xvc1 # {{{
implements tence_cmdline_exitValueComputer
{
    const 
PASS      0;
    const 
FAILURE   1;
    const 
EXCEPTION 2;
    const 
DEFECT    4;
    public static function 
compute(Tence_Reporter $tr# {{{
    
{
        
$rv self::PASS;
        if (
$tr->failures()) {
            
$rv self::FAILURE;
        }
        if (
$tr->exceptions()) {
            
$rv |= self::EXCEPTION;
        }
        if (
$tr->defects()) {
            
$rv |= self::DEFECT;
        }
        return 
$rv;
    } 
# }}}
# }}}

class tence_cmdline
{
    private 
$config_t$testlist_t$lister_t$runner_t$loader_t;
    private 
$xvc;
    public function 
__construct# {{{
        
$config_t 'tence_cmdline_Config',
        
$runner_t 'Tence_BasicRunner',
        
$loader_t 'Tence_BasicLoader',
        
tence_cmdline_exitValueComputer $xvc null,
        
$testlist_t 'tence_TestList',
        
tence_TestLister $lister null
    
)
    { 
# {{{
        
$this->config_t $config_t;
        
$this->runner_t $runner_t;
        
$this->loader_t $loader_t;
        
$this->testlist_t $testlist_t;
        if (
is_null($lister)) {
            
$lister = new tence_TestLister;
        }
        if (
is_null($xvc)) {
            
$xvc = new tence_cmdline_xvc1;
        }
        
$this->xvc $xvc;
        
$this->lister $lister;
    } 
# }}} # }}}
    
protected function getConfig($argv null# {{{
    
{
        return 
$this->instantiate($this->config_t, array($argv));
    } 
# }}}
    
protected function listTests($file# {{{
    
{
        return 
$this->lister->list_(
            
$this->instantiate($this->testlist_t, array($file))
        );
    } 
# }}}
    
private function instantiate($class, array $args = array()) # {{{
    
{
        
$rc = new ReflectionClass($class);
        
$argc func_num_args();
        if (
== $argc) {
            return 
$rc->newInstanceArgs($args);
        }
        return 
$rc->newInstance();
    } 
# }}}
    
protected function runTests(Tence_RunnerConfig $cfg# {{{
    
{
        
$runner $this->instantiate(
            
$this->runner_t,
            array(
$this->instantiate($this->loader_t))
        );
        
$runner->run($cfg);
        return 
$this->xvc->compute($cfg->reporter());
    } 
# }}}
    
protected function displayVersion($myname# {{{
    
{
        
printf("%s %s\n"$mynameTENCE_VERSIONSTRING);
        return 
0;
    } 
# }}}
    
public function main(array $argv# {{{
    
{
        
$myname basename($argv[0]);
        try {
            
$cfg $this->getConfig($argv);
            if (
$cfg->version()) {
                return 
$this->displayVersion($myname);
            }
            if (
null !== ($file $cfg->testListFor())) {
                return 
$this->listTests($file);
            }
            return 
$this->runTests($cfg);
        } catch (
Exception $e) {
            
fprintf(STDERR"%s: %s\n"$myname$e->getMessage());
            return 
99;
        }
    } 
# }}}
}

# vim: ft=php et ts=4 sts=4 sw=4 fdm=marker cms=\ #\ %s