<?php
namespace Tlf;
/**
* Base class for test classes
* @note main class file executes tests. Traits contain everything you use INSIDE a test
*/
class Tester {
use Tester\Assertions;
use Tester\Exceptions;
use Tester\Databasing;
use Tester\Utilities;
use Tester\Server;
use Tester\Other;
protected $catchers = [];
/**
* Comparisons from a single test. Should be reset between tests.
*/
protected $assertions = ['pass'=> 0, 'fail'=>0];
protected $enabled = true;
protected $options = [];
/**
* The cli class used to run the tests
*/
public $cli = null;
/**
* The string name of the method being called for the current test. Like `"testSomething"`
*/
public ?string $current_test = null;
/**
* @param $options usually args passed from the command line.
*/
public function __construct(array $options=[], $cli=null){
$this->backward_compatability();
$this->options = $options;
if ($this->options['set_error_handler']??true){
set_error_handler([$this,'throwError']);
}
$this->cli = $cli;
$this->prepare();
}
public function throwError($errno, $errstr, $errfile, $errline) {
throw new \ErrorException($errstr, $errno, 0, $errfile, $errline);
}
/**
* @deprecated This method will be removed in v0.4
*/
public function backward_compatability(){}
/**
* Get array of test methods names
* @return array like `['testMethodOne', 'testMethodTwo']`
*/
public function get_test_methods(){
$list = [];
foreach (get_class_methods($this) as $method){
if ($method=='test')continue;
if (substr($method,0,4)!='test')continue;
$list[] = $method;
}
return $list;
}
/**
* get a readable name from a test method name
*/
public function get_test_name($method_name): string{
$name = $method_name;
if (substr($method_name,0,4)=='test')$name = substr($method_name,4);
return $name;
}
public function run_test_method($method){
$name = $this->get_test_name($method);
if ($this->options['test']!=[]
&&!in_array($name, $this->options['test'])){
return false;
}
$test =
[
'method'=>$method,
'error'=>null,
'name'=>$name,
'pass'=>false,
'enabled'=>$this->enabled,
];
$this->assertions = ['pass'=>0, 'fail'=>0];
$this->catchers = [];
$bench_start = microtime(true);
$ob_level = $this->startOb();
try {
$this->current_test = $method;
$this->$method();
} catch (\Throwable $t){
$test['error'] = $t->__toString();
echo $test['error'];
}
$this->current_test = null;
$test['enabled'] = $this->enabled;
$test['assertions'] = $this->assertions;
$test['output'] = $this->endOb($ob_level);
$test['bench'] = $this->benchEnd($bench_start);
if ($this->assertions['pass']>=1
&&$this->assertions['fail']===0
&&$test['error'] === null
){
$test['pass'] = true;
}
return $test;
}
public function print_test_results($test){
$status = $test['pass'] ? 'PASS' : 'FAIL';
$symbol = $test['pass'] ? '+' : '-';
if (in_array($test['name'], $this->options['test'])){
if ($test['enabled']!=true)$symbol = '/';
$str = str_repeat($symbol, 15);
echo "\n$str ".$test['name']."[start] $str\n";
echo $test['output'];
if (($c=count($this->catchers))>0){
echo "\n\n EXCEPTION FAIL:{$c} exceptions were not handled.";
}
$class = get_class($this);
echo "\n$str ".$test['name']."[end] ($class) $str";
return;
}
if ($test['enabled']!=true)$symbol = '//';
// $assertions =
// '+'.$test['assertions']['pass']
// .', -'.$test['assertions']['fail'];
// ;
$bench = '';
if ($test['bench']['diff']>$this->options['bench.threshold']){
$ms = $test['bench']['diff'] * 1000;
$ms = number_format($ms,4);
$bench=' '.$ms.'ms';
}
echo "\n $symbol ".$test['name']. $bench; //." ($assertions)";
}
/**
* Run tests
*
* @param $methods an array of method names to run as tests or NULL to run all methods beginning with 'test'
*/
public function run(){
$class = explode('\\',get_class($this));
$name = array_pop($class);
echo "\n". array_pop($class).'\\'.$name.': ';
$methods = $this->get_test_methods();
$results = [
'class'=>get_class($this),
'tests_run'=>0,
'pass'=>0,
'fail'=>0,
'disabled'=>0,
];
$tests = [];
foreach ($methods as $method){
$this->inverted = false;
$this->enabled = true;
$test = $this->run_test_method($method);
if ($test===false)continue;
$this->print_test_results($test);
$tests[] = $test;
$results['tests_run']++;
if ($test['enabled']!==true){
$results['disabled']++;
} else if ($test['pass']===true){
$results['pass']++;
} else {
$results['fail']++;
}
$test['enabled'] = $this->enabled;
}
// echo "\n ".$results['fail'].' fail, '.$results['pass'].' pass';;
$results['tests'] = $tests;
return $results;
}
/**
* @param $start_time a value from `microtime(true)`
*/
public function benchEnd($start_time){
$end = microtime(true);
$diff = $end - $start_time;
return [
'start'=>$start_time,
'end'=>$end,
'diff'=>$diff
];
}
}