src.dualinventive.com/dinet/common/ditest-php/test_framework.php

645 lines
17 KiB
PHP
Executable File

<?php
function debug($var) {
$trace = DiTest::trace(array('start' => 1, 'depth' => 2, 'format' => 'array'));
$file = str_replace(dirname(dirname(dirname(__FILE__))) . '/', '', $trace[0]['file']);
$line = $trace[0]['line'];
echo PHP_EOL . $file . ' (line ' . $line . ')' . PHP_EOL;
echo '########## DEBUG ##########' . PHP_EOL;
var_dump($var);
echo '###########################' . PHP_EOL . PHP_EOL;
}
require_once(__DIR__ . "/DiTestColor.php");
require_once(__DIR__ . "/DiTestMessage.php");
class DiTest {
const StepSkip = true;
public $errorCount = 0;
private $__stepSkip = false;
private $__stepBusy = false;
private $__startCounter;
private $__step;
private $__stepCounter = 0;
private $__testname = "";
private $__stepCount = 0; /* Total tests run */
private $__passCount = 0; /* Total tests passed */
private $__skipCount = 0; /* Total tests skipped */
private $__finishedAlready = false; /* Prevent double print of summary */
private $__qpdGen = false;
private $__report = null;
private function incSteps() {
$this->__stepCount++;
}
private function incSkip() {
$this->__skipCount++;
}
private function array_diff_recursive($aArray1, $aArray2) {
$aReturn = array();
foreach ($aArray1 as $mKey => $mValue) {
if (array_key_exists($mKey, $aArray2)) {
if (is_array($mValue)) {
$aRecursiveDiff = $this->array_diff_recursive($mValue, $aArray2[$mKey]);
if (count($aRecursiveDiff)) {
$aReturn[$mKey] = $aRecursiveDiff;
}
} else {
if ($mValue != $aArray2[$mKey]) {
$aReturn[$mKey] = $mValue;
}
}
} else {
$aReturn[$mKey] = $mValue;
}
}
return $aReturn;
}
private function fail($msg, $depthIncr = 0, $trace = true) {
if ($this->__stepSkip) {
$this->skip($msg);
return;
}
DiTestMsg::fail($msg);
if ($trace) {
$trace = DiTest::trace(array('start' => 2 + $depthIncr, 'depth' => 3 + $depthIncr, 'format' => 'array'));
$file = str_replace(dirname(dirname(dirname(__FILE__))) . '/', '', $trace[0]['file']);
$line = $trace[0]['line'];
echo ' Occurred in: ' . $file . ' (line ' . $line . ')' . PHP_EOL;
}
$this->errorCount++;
$this->assert($this->__assert);
}
private function assert($assert = false) {
if ($assert) {
$this->__assert = false;
$ret = $this->finish(true);
}
}
private function pass($msg) {
if ($this->__stepSkip) {
$this->skip($msg);
return;
}
$this->__passCount++;
DiTestMsg::pass($msg);
}
private function skip($msg) {
$this->incSkip();
DiTestMsg::skip($msg);
}
private function __urlSafe($string) {
return preg_replace('/^-+|-+$/', '', strtolower(preg_replace('/[^a-zA-Z0-9\.]+/', '-', $string)));
}
public function __construct($testname = false, $assert = false, $reportGen = false) {
if ($testname) {
$this->__testname = $testname;
DiTestMsg::info("");
DiTestMsg::info(str_repeat("=", strlen($testname)));
DiTestMsg::info($testname);
DiTestMsg::info(str_repeat("=", strlen($testname)));
DiTestMsg::info("");
if ($reportGen) {
$this->__qpdGen = fopen($this->__urlSafe($testname) . '.txt', "w");
$this->__report = new TestReport($testname, $this->__urlSafe($testname));
}
}
$this->__assert = $assert;
$this->errorCount = 0;
}
public function __destruct() {
$this->finish();
}
public function info($msg) {
DiTestMsg::info($msg);
}
public function goal($goalStr) {
if (!is_null($this->__report) && strlen($goalStr)) {
$this->__report->goal($goalStr);
}
DiTestMsg::info("");
DiTestMsg::info("Goal:");
DiTestMsg::info($goalStr);
DiTestMsg::info("");
}
public function stepToQpd($str) {
if ($this->__qpdGen !== false) {
fwrite($this->__qpdGen, $this->__stepCounter . ". " . trim(preg_replace('/^([0-9]*)\./', '', $str)) . "\n");
}
}
public function step($str, $skip = false) {
if ($this->__stepBusy)
$this->endStep('', true);
$this->__step = $str;
$this->__stepCounter++;
$this->__startCounter = $this->errorCount;
$this->__stepBusy = true;
$this->__stepSkip = $skip;
DiTestMsg::run($this->__step);
$this->stepToQpd($this->__step);
}
public function endStep($str = '', $autoEnd = false) {
$str = $str;
$this->__stepBusy = false;
if ($this->__startCounter == $this->errorCount) {
if ($this->__report !== null) $this->__report->stepFinish($this->__step, true);
DiTestMsg::pass($this->__step);
} else {
if ($this->__report !== null) $this->__report->stepFinish($this->__step, false);
DiTestMsg::fail(($this->errorCount - $this->__startCounter) . ' failure(s) for test "' . $this->__step . '"', 0, false);
}
}
public function rpcSend($identified, $request, $json = true) {
if (isset ($request['req'])){
DiTestMsg::run("Request '" . $request['req'] . "'");
} elseif (isset($request['rep'])) {
DiTestMsg::run("Reply '" . $request['rep'] . "'");
}
if ($json) {
$identified->send(json_encode($request));
} else {
$identified->send($request);
}
}
public function rpcRecv($identified) {
DiTestMsg::run("Receiving message...");
while (true) {
$r = $identified->recv();
if (!$identified->getSockOpt(ZMQ::SOCKOPT_RCVMORE)) {
break;
}
}
$message = json_decode($r, true);
print_r($message);
return $message;
}
public function rpcRepOk($identified, $request = null, $replyMsg = false) {
if ($request !== null) {
DiTestMsg::run("Perform '" . $request['req'] . "'");
$identified->send(json_encode($request));
}
$message = json_decode($identified->recv(), true);
if (isset($message['error'])) {
$this->fail("Error occurred, error member found");
return false;
}
$this->__passCount++;
if (!$replyMsg) {
print_r($message);
return $this->errorCount;
}
return $message;
}
public function rpcRepError($identified, $request = null, $expectedError, $json = true) {
$errorCnt = 0;
if ($request !== null) {
DiTestMsg::run("Perform '" . (isset($request['req']) ? $request['req'] : '<empty>') . "'");
if ($json)
$identified->send(json_encode($request));
else
$identified->send($request);
}
$message = json_decode($identified->recv(), true);
if (!isset($message['error'])) {
$this->errorCount++;
$this->fail("Error occurred, error member NOT found");
} elseif($message['error']['code'] != $expectedError) {
$this->errorCount++;
$this->fail("Error occurred, expected code " . $expectedError . ", got " . $message['error']['code']);
} else {
print_r($message);
$this->__passCount++;
}
return $message;
}
public function confirm($question, $positive = 'y', $negative = 'n') {
$handle = fopen ("php://stdin","r");
DiTestMsg::user($question . " [".$positive."/".$negative."]: ");
$exitLoop = false;
while (!$exitLoop && $line = fgets($handle)) {
$line = trim($line);
switch($line) {
case $negative:
$this->fail("Invalid confirm: \"$line\"");
case $positive:
$this->__passCount++;
$exitLoop = true;
break;
default:
DiTestMsg::user("Please type '".$positive."' or '".$negative."' [".$positive."/".$negative."]: ");
break;
}
}
fclose($handle);
}
public function user($msg) {
DiTestMsg::user($msg);
}
public function anykey($msg) {
$handle = fopen("php://stdin","r");
DiTestMsg::user($msg . ", press [enter] to continue...");
fgets($handle);
}
public function passed($msg) {
DiTestMsg::pass($msg);
}
public function failed($msg) {
DiTestMsg::fail($msg);
}
public function msleep($msg, $ms) {
DiTestMsg::wait("[$ms ms] " . $msg);
usleep($ms * 1000);
}
public function sleep($msg, $seconds) {
return $this->msleep($msg, $seconds * 1000);
}
public function reset() {
DiTestMsg::run("Resetting Test-statistics");
$this->__stepCount = 0;
$this->__passCount = 0;
$this->errorCount = 0;
$this->__skipCount = 0;
}
public function isTrue($actual) {
$this->incSteps();
if ($actual) {
$this->__passCount++;
return true;
}
$this->fail("Expected true, got '" . (($actual) ? 'true' : 'false') . "'");
return false;
}
public function isFalse($actual) {
$this->incSteps();
if (!$actual) {
$this->__passCount++;
return true;
}
$this->fail("Expected false, got '" . (($actual) ? 'true' : 'false') . "'");
return false;
}
public function failure($msg) {
$this->incSteps();
$this->fail($msg);
}
// timeNow gets the current UNIX time in milliseconds
public function timeNow() {
return (int)microtime(true) * 1000;
}
// timeSince calculates the time since the given start time
public function timeSince(int $start) {
if ($start < 0)
return 0;
$now = $this->timeNow();
if ($start > $now)
return 0;
$since = $now - $start;
return $since;
}
// isTimeWithin validates if the current time is within the window from start
public function isTimeWithin(int $start, int $window) {
if ($start <= 0 || $window <= 0)
return false;
$end = $start + $window;
$now = $this->timeNow();
if ($now <= $end)
return true;
return false;
}
public function equal($expected, $actual, $assert = false) {
$this->incSteps();
if (is_array($expected)) {
if (is_array($actual)) {
$diff = $this->array_diff_recursive($expected, $actual);
$diff += $this->array_diff_recursive($actual, $expected);
if (count($diff)) {
$this->fail("Expected and actual array differ, expected:");
var_dump($expected);
$this->failed("got:");
var_dump($actual);
$this->assert($assert);
return false;
}
$this->__passCount++;
return true;
}
$this->fail("Expected array, got '" . $actual . "'");
$this->assert($assert);
return false;
}
if ($expected == $actual) {
$this->__passCount++;
return true;
}
if (is_bool($actual)) {
$actual = ($actual) ? 'true' : 'false';
}
if (is_bool($expected)) {
$expected = ($expected) ? 'true' : 'false';
}
$this->fail("Expected '" . $expected . "', got '" . $actual . "'");
$this->assert($assert);
return false;
}
public function notEqual($expected, $actual) {
$this->incSteps();
if (is_array($expected)) {
if (is_array($actual)) {
$diff = $this->array_diff_recursive($expected, $actual);
$diff += $this->array_diff_recursive($actual, $expected);
if (count($diff)) {
$this->__passCount++;
return true;
}
$this->fail("Arrays are identical");
return false;
}
$this->__passCount++;
return true;
}
if ($expected != $actual) {
$this->__passCount++;
return true;
}
$this->fail("Expected something else then '" . $expected . "', got '" . $actual . "'");
return false;
}
public function range($actual, $min, $max) {
$this->incSteps();
if (($actual >= $min) && ($actual <= $max)) {
$this->__passCount++;
return true;
} else {
if ($actual < $min) {
$delta = $actual - $min;
$sign = "";
} else if ($actual > $max) {
$delta = $actual - $max;
$sign = "+";
} else {
$delta = "?";
$sign = "?";
}
$this->fail("Expect >= $min && <= $max, got $actual (delta: $sign$delta)");
return false;
}
}
// lte checks if the actual value is less then or equal to max (value <= max)
public function lte($actual, $max) {
$this->incSteps();
if ($actual <= $max) {
$this->__passCount++;
return true;
} else {
if ($actual > $max) {
$delta = $actual - $max;
$sign = "+";
} else {
$delta = "?";
$sign = "?";
}
$this->fail("Expect <= $max, got $actual (delta: $sign$delta)");
return false;
}
}
/**
* Outputs a stack trace based on the supplied options.
*
* ### Options
*
* - `depth` - The number of stack frames to return. Defaults to 999
* - `format` - The format you want the return. Defaults to the currently selected format. If
* format is 'array' or 'points' the return will be an array.
* - `args` - Should arguments for functions be shown? If true, the arguments for each method call
* will be displayed.
* - `start` - The stack frame to start generating a trace from. Defaults to 0
*
* @param array $options Format for outputting stack trace
* @return mixed Formatted stack trace
* @link http://book.cakephp.org/2.0/en/development/debugging.html#Debugger::trace
*/
public static function trace($options = array()) {
$_templates = array(
'log' => array(
'trace' => '{:reference} - {:path}, line {:line}',
'error' => "{:error} ({:code}): {:description} in [{:file}, line {:line}]"
),
'js' => array(
'error' => '',
'info' => '',
'trace' => '<pre class="stack-trace">{:trace}</pre>',
'code' => '',
'context' => '',
'links' => array(),
'escapeContext' => true,
),
'html' => array(
'trace' => '<pre class="cake-error trace"><b>Trace</b> <p>{:trace}</p></pre>',
'context' => '<pre class="cake-error context"><b>Context</b> <p>{:context}</p></pre>',
'escapeContext' => true,
),
'txt' => array(
'error' => "{:error}: {:code} :: {:description} on line {:line} of {:path}\n{:info}",
'code' => '',
'info' => ''
),
'base' => array(
'traceLine' => '{:reference} - {:path}, line {:line}',
'trace' => "Trace:\n{:trace}\n",
'context' => "Context:\n{:context}\n",
)
);
$defaults = array(
'depth' => 999,
'format' => 'txt',
'args' => false,
'start' => 0,
'scope' => null,
'exclude' => array('call_user_func_array', 'trigger_error')
);
foreach ($options as $k => $option) {
$defaults[$k] = $option;
}
$options = $defaults;
$backtrace = debug_backtrace();
$count = count($backtrace);
$back = array();
$_trace = array(
'line' => '??',
'file' => '[internal]',
'class' => null,
'function' => '[main]'
);
for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) {
$trace = array_merge(array('file' => '[internal]', 'line' => '??'), $backtrace[$i]);
$signature = $reference = '[main]';
if (isset($backtrace[$i + 1])) {
$next = array_merge($_trace, $backtrace[$i + 1]);
$signature = $reference = $next['function'];
if (!empty($next['class'])) {
$signature = $next['class'] . '::' . $next['function'];
$reference = $signature . '(';
if ($options['args'] && isset($next['args'])) {
$args = array();
foreach ($next['args'] as $arg) {
$args[] = Debugger::exportVar($arg);
}
$reference .= implode(', ', $args);
}
$reference .= ')';
}
}
if (in_array($signature, $options['exclude'])) {
continue;
}
if ($options['format'] === 'points' && $trace['file'] !== '[internal]') {
$back[] = array('file' => $trace['file'], 'line' => $trace['line']);
} elseif ($options['format'] === 'array') {
$back[] = $trace;
} else {
if (isset($_templates[$options['format']]['traceLine'])) {
$tpl = $_templates[$options['format']]['traceLine'];
} else {
$tpl = $_templates['base']['traceLine'];
}
$trace['path'] = static::trimPath($trace['file']);
$trace['reference'] = $reference;
unset($trace['object'], $trace['args']);
$back[] = CakeText::insert($tpl, $trace, array('before' => '{:', 'after' => '}'));
}
}
if ($options['format'] === 'array' || $options['format'] === 'points') {
return $back;
}
return implode("\n", $back);
}
private function exitTestSummary() {
$summary = DiTestColor::set("[ Summary ]", "bold");
DiTestMsg::info("");
DiTestMsg::info($summary . " [ PASS ] : $this->__passCount");
DiTestMsg::info($summary . " [ FAIL ] : $this->errorCount");
DiTestMsg::info($summary . " [ SKIP ] : $this->__skipCount");
DiTestMsg::info($summary . " Total compares: $this->__stepCount");
if ($this->errorCount > 0) {
DiTestMsg::fail($summary . " Test \"$this->__testname\" has $this->errorCount error(s)", 0, false);
} else {
DiTestMsg::pass($summary . " Test \"$this->__testname\" has no errors reported");
}
}
/* Get summary of tests */
public function summary() {
return array(
"pass" => $this->__passCount,
"fail" => $this->errorCount,
"skip" => $this->__skipCount);
}
public function finish($exit = true) {
$ret = ($this->errorCount > 0) ? 1 : 0;
if ($this->__finishedAlready == true)
return $ret;
$this->__finishedAlready = true;
$this->__assert = false; // This prevents calling ourselfs to many times...
if ($this->__stepBusy)
$this->endStep('', true);
if ($this->__report !== null) $this->__report->writeReport($this->summary());
if ($this->__qpdGen)
fclose($this->__qpdGen);
$this->exitTestSummary();
usleep(50 * 1000);
if ($ret)
exit($ret);
else
return $ret;
}
}
require_once(__DIR__ . "/TestReport.php");
require_once(__DIR__ . "/predis-1.0.3/src/Autoloader.php");
require_once(__DIR__ . "/websocket_client.php");
require_once(__DIR__ . "/equipment/can.php");
require_once(__DIR__ . "/equipment/psu_cpx400.php");
require_once(__DIR__ . "/equipment/psu_labps3005d.php");
require_once(__DIR__ . "/equipment/psu_labps3005d_tcp.php");
require_once(__DIR__ . "/equipment/serial_port.php");
if (extension_loaded("zmq")) {
require_once(__DIR__ . "/rpc/RequestSocket.php");
require_once(__DIR__ . "/rpc/ReplySocket.php");
require_once(__DIR__ . "/rpc/PublishSocket.php");
require_once(__DIR__ . "/rpc/PairSocket.php");
require_once(__DIR__ . "/rpc/SubscribeSocket.php");
}
require_once(__DIR__ . "/rpc/constants.php");
Predis\Autoloader::register();