595 lines
16 KiB
PHP
595 lines
16 KiB
PHP
<?php
|
|
|
|
function debug($var) {
|
|
$t= new DiTest();
|
|
$trace = $t->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 = $this->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 = true) {
|
|
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:
|
|
$exitLoop = true;
|
|
break;
|
|
default:
|
|
DiTestMsg::user("Please type '".$positive."' or '".$negative."' [".$positive."/".$negative."]: ");
|
|
break;
|
|
}
|
|
}
|
|
fclose($handle);
|
|
}
|
|
|
|
public function anykey($msg) {
|
|
$handle = fopen("php://stdin","r");
|
|
DiTestMsg::user($msg . ", press a key 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Templates used when generating trace or error strings. Can be global or indexed by the format
|
|
* value used in $_outputFormat.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_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",
|
|
)
|
|
);
|
|
|
|
/**
|
|
* 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 function trace($options = array()) {
|
|
$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($this->_templates[$options['format']]['traceLine'])) {
|
|
$tpl = $this->_templates[$options['format']]['traceLine'];
|
|
} else {
|
|
$tpl = $this->_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");
|
|
|
|
if (extension_loaded("zmq")) {
|
|
require_once(__DIR__ . "/rpc/ReplySocket.php");
|
|
require_once(__DIR__ . "/rpc/PublishSocket.php");
|
|
require_once(__DIR__ . "/rpc/PairSocket.php");
|
|
}
|
|
|
|
Predis\Autoloader::register();
|