1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649:
<?php
require_once(__DIR__.'/../Tipy.php');
class AssertionFailedException extends TipyException {}
class TipyTestCase {
private $tests;
private $assertions;
private $exeptions;
private $failures;
public $transactionalFixtures = true;
public function __construct() {
$this->tests = 0;
$this->assertions = 0;
$this->failures = [];
$this->exceptions = [];
}
public function run() {
$className = get_class($this);
$methods = get_class_methods($className);
foreach ($methods as $testName) {
if (!preg_match("/^test/", $testName)) {
continue;
}
$this->tests++;
$this->clearAppContext();
$this->setUp();
$testClosure = function() use ($testName) {
try {
$this->$testName();
echo TipyCli::green('.');
} catch (AssertionFailedException $e) {
$this->failures[] = $e;
echo TipyCli::purple('F');
} catch (Exception $e) {
$this->exceptions[] = $e;
echo TipyCli::red('E');
}
};
if ($this->transactionalFixtures) {
TipyDAO::transaction(function() use ($testName, $testClosure) {
$testClosure();
TipyDAO::rollback();
});
} else {
$testClosure();
}
$this->tearDown();
}
}
private function clearAppContext() {
$app = TipyApp::getInstance();
$app->in->clear();
$app->session->clear();
}
public function setUp() {
}
public function tearDown() {
}
public static function applyFixture($db, $filename) {
$content = file_get_contents($filename, 1);
if ($content) {
$querys = explode(';', $content);
foreach ($querys as $query) {
if (trim($query)) {
$db->query($query) or die('Fixture error!'.PHP_EOL.$query.PHP_EOL);
}
}
}
}
private function varDump($var) {
ob_start();
var_dump($var);
$var = ob_get_clean();
$var = preg_replace('/\n$/', '', $var);
return $var;
}
private function assertion($result, $message) {
$this->assertions++;
if (!$result) {
$trace = debug_backtrace();
$args = $trace[1]['args'];
$actual = $this->varDump($args[0]);
if (sizeof($args) > 1) {
$expected = $this->varDump($args[1]);
$message = $actual." ".$message." ".$expected;
} else {
$message = $actual." ".$message;
}
$e = new AssertionFailedException($message);
throw $e;
}
}
public function assertEqual($actual, $expected) {
$this->assertion($actual == $expected, "expected to be equal");
}
public function assertNotEqual($actual, $expected) {
$this->assertion($actual <> $expected, "expected not be equal");
}
public function assertIdentical($actual, $expected) {
$this->assertion($actual === $expected, "expected to be the identical");
}
public function assertNull($actual) {
$this->assertion($actual === null, "expected to be NULL");
}
public function assertNotNull($actual) {
$this->assertion($actual !== null, "expected not to be NULL");
}
public function assertTrue($actual) {
$this->assertion($actual === true, "expected to be true");
}
public function assertFalse($actual) {
$this->assertion($actual === false, "expected to be false");
}
public function assertThrown($exceptionClass, $exceptionMessage, $closure) {
$this->assertions++;
$expected = $exceptionClass.": ".$exceptionMessage;
try {
$closure();
throw new AssertionFailedException('"'.$expected.'" expected but nothing was thrown');
} catch (AssertionFailedException $e) {
throw $e;
} catch (Exception $e) {
$actual = get_class($e).": ".$e->getMessage();
if ($expected != $actual) {
throw new AssertionFailedException('"'.$expected.'" expected but "'.$actual.'" was thrown');
}
}
}
public function assertThrownRegexp($expectedClassString, $expectedMessageRegexp, $closure) {
$this->assertions++;
$expected = $expectedClassString.": ".$expectedMessageRegexp;
try {
$closure();
throw new AssertionFailedException('"'.$expected.'" expected but nothing was thrown');
} catch (AssertionFailedException $e) {
throw $e;
} catch (Exception $e) {
$actualClass = get_class($e);
$actualMessage = $e->getMessage();
if (($expectedClassString != $actualClass) || !preg_match($expectedMessageRegexp, $actualMessage)) {
$actual = $actualClass.': '.$actualMessage;
throw new AssertionFailedException('"'.$expected.'" does not match "'.$actual.'"');
}
}
}
public function execute($controllerName, $actionName, &$output) {
$app = TipyApp::getInstance();
$app->in->set('controller', $controllerName);
$app->in->set('action', $actionName);
$app->out->clear();
$output = "";
ob_start();
$app->run();
$output = ob_get_clean();
}
public function getSummary() {
return [
"tests" => $this->tests,
"assertions" => $this->assertions,
"failures" => $this->failures,
"exceptions" => $this->exceptions
];
}
}
class TipyTestRunner {
public $fixtures;
private $tests;
private $assertions;
private $failures;
private $exeptions;
private $testNames;
private $testFiles;
private $fixtureFiles;
private $workingDir;
private $args;
public $skipDirs = ['.git', '.svn', 'vendor'];
public function __construct() {
$this->tests = 0;
$this->assertions = 0;
$this->failures = [];
$this->exceptions = [];
$this->testNames = [];
$this->testFiles = [];
$this->fixtures = [];
$this->fixtureFiles = [];
if (!CLI_MODE) {
die("Tests should be run from command line.".PHP_EOL);
}
$args = $_SERVER['argv'];
array_shift($args);
if (sizeof($args) == 0) {
$args = [getcwd()];
}
$this->args = $args;
$this->findWorkingDir();
echo '(in '.$this->workingDir.')'.PHP_EOL;
$this->findConfig();
foreach ($args as $filename) {
$this->findTests($filename);
}
}
private function findWorkingDir() {
foreach ($this->args as $filename) {
if (preg_match('/^(.*tests)(\/|$)/', realpath($filename), $matches) && is_dir($matches[1])) {
$this->workingDir = $matches[1];
return;
}
if (is_dir($filename.'/tests')) {
return $this->workingDir = realpath($filename.'/tests');
}
}
die('No tests found.'.PHP_EOL);
}
private function findConfig() {
if (defined('INI_FILE')) {
return;
}
if (getenv('CIRCLECI') && file_exists($this->workingDir.'/config.ini.ci')) {
define('INI_FILE', $this->workingDir.'/config.ini.ci');
return;
}
if (file_exists($this->workingDir.'/config.ini')) {
define('INI_FILE', $this->workingDir.'/config.ini');
return;
}
if (file_exists($this->workingDir.'/../config.ini')) {
define('INI_FILE', realpath($this->workingDir.'/../config.ini'));
return;
}
}
private function findFixtures() {
if (sizeof($this->fixtures) == 0) {
$searchDirs = $this->args;
} else {
$searchDirs = $this->fixtures;
}
foreach($searchDirs as $filename) {
$this->findFixtureFiles($filename);
}
if (sizeof($this->fixtureFiles) == 0) {
$this->findFixtureFiles($this->workingDir);
}
}
private function findFixtureFiles($filename) {
if (is_dir($filename)) {
if ($handle = opendir($filename)) {
while (false !== ($f = readdir($handle))) {
if (!preg_match('/\.$/', $f)) {
$this->findFixtureFiles($filename.'/'.$f);
}
}
closedir($handle);
}
} else if (preg_match('/\.sql$/', $filename)) {
$this->fixtureFiles[] = $filename;
}
}
private function findTests($filename) {
if (in_array(basename($filename), $this->skipDirs)) {
return;
}
if (is_dir($filename)) {
if ($handle = opendir($filename)) {
while (false !== ($f = readdir($handle))) {
if (!preg_match('/\.$/', $f)) {
$this->findTests($filename.'/'.$f);
}
}
closedir($handle);
}
} else if (preg_match('/(test\w+|\w+Test)\.php$/', $filename, $matches)) {
$testName = $matches[1];
$this->testNames[] = $testName;
$this->testFiles[$testName] = $filename;
}
}
private function requireAutoloads() {
if (file_exists($this->workingDir.'/autoload.php')) {
require_once($this->workingDir.'/autoload.php');
}
if (file_exists($this->workingDir.'/../app/autoload.php')) {
require_once($this->workingDir.'/../app/autoload.php');
}
}
public function run() {
$this->findFixtures();
$app = TipyApp::getInstance();
$app->connectToDb();
$app->db->query('DROP DATABASE IF EXISTS '.$app->config->get('db_test_name'));
$app->db->query('CREATE DATABASE '.$app->config->get('db_test_name'));
$app->db->select_db($app->config->get('db_test_name'));
foreach ($this->fixtureFiles as $fixture) {
TipyTestCase::applyFixture($app->db, $fixture);
}
$this->requireAutoloads();
echo PHP_EOL;
foreach ($this->testNames as $test) {
require_once($this->testFiles[$test]);
$test = new $test;
$test->run();
$this->updateSummary($test->getSummary());
unset($test);
}
$this->printSummary();
if (sizeof($this->failures) + sizeof($this->exceptions) == 0) {
return 0;
} else {
return 1;
}
}
private function updateSummary($summary) {
$this->tests += $summary['tests'];
$this->assertions += $summary['assertions'];
foreach ($summary['failures'] as $failure) {
$this->failures[] = $failure;
}
foreach ($summary['exceptions'] as $exception) {
$this->exceptions[] = $exception;
}
return $summary;
}
private function printSummary() {
echo PHP_EOL.PHP_EOL;
echo "Tests: ".$this->tests;
echo ", Assertions: ".$this->assertions;
echo ", Failures: ".sizeof($this->failures);
echo ", Exceptions: ".sizeof($this->exceptions);
echo PHP_EOL.PHP_EOL;
if (sizeof($this->failures) > 0) {
echo TipyCli::red('Failures:').PHP_EOL;
$i = 0;
foreach ($this->failures as $e) {
$i++;
echo $i.") ";
$trace = $e->getTrace();
if ($trace[2]['function'] == "{closure}") {
$test = $trace[1];
$testBody = $trace[0];
} else {
$test = $trace[2];
$testBody = $trace[1];
}
echo TipyCli::yellow($test['function']).": ";
echo $testBody['file']." at line (".TipyCli::cyan($testBody['line']).")".PHP_EOL;
echo $e->getMessage();
echo PHP_EOL.PHP_EOL;
}
}
if (sizeof($this->exceptions) > 0) {
echo TipyCli::red('Exceptions:').PHP_EOL;
$i = 0;
foreach ($this->exceptions as $e) {
$i++;
echo $i.") ";
$trace = $e->getTrace();
echo TipyCli::yellow($trace[0]['function']).": ";
echo $e->getFile()." at line (".TipyCli::cyan($e->getLine()).")".PHP_EOL;
echo get_class($e).": ".$e->getMessage().PHP_EOL;
echo $e->getTraceAsString();
echo PHP_EOL.PHP_EOL;
}
}
}
}