PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/test.php

http://github.com/mageekguy/atoum
PHP | 1783 lines | 1476 code | 307 blank | 0 comment | 90 complexity | eb9cab495b88a8b42163ef574aa89459 MD5 | raw file
  1. <?php
  2. namespace mageekguy\atoum;
  3. use
  4. mageekguy\atoum,
  5. mageekguy\atoum\test,
  6. mageekguy\atoum\mock,
  7. mageekguy\atoum\asserter,
  8. mageekguy\atoum\asserters,
  9. mageekguy\atoum\exceptions,
  10. mageekguy\atoum\annotations
  11. ;
  12. abstract class test implements observable, \countable
  13. {
  14. const testMethodPrefix = 'test';
  15. const defaultNamespace = '#(?:^|\\\)tests?\\\units?\\\#i';
  16. const defaultMethodPrefix = '#^(?:test|_*[^_]+_should_)#i';
  17. const runStart = 'testRunStart';
  18. const beforeSetUp = 'beforeTestSetUp';
  19. const afterSetUp = 'afterTestSetUp';
  20. const beforeTestMethod = 'beforeTestMethod';
  21. const fail = 'testAssertionFail';
  22. const error = 'testError';
  23. const void = 'testVoid';
  24. const uncompleted = 'testUncompleted';
  25. const skipped = 'testSkipped';
  26. const exception = 'testException';
  27. const runtimeException = 'testRuntimeException';
  28. const success = 'testAssertionSuccess';
  29. const afterTestMethod = 'afterTestMethod';
  30. const beforeTearDown = 'beforeTestTearDown';
  31. const afterTearDown = 'afterTestTearDown';
  32. const runStop = 'testRunStop';
  33. const defaultEngine = 'concurrent';
  34. const enginesNamespace = '\mageekguy\atoum\test\engines';
  35. private $score = null;
  36. private $locale = null;
  37. private $adapter = null;
  38. private $mockGenerator = null;
  39. private $mockAutoloader = null;
  40. private $factoryBuilder = null;
  41. private $reflectionMethodFactory = null;
  42. private $asserterGenerator = null;
  43. private $assertionManager = null;
  44. private $phpMocker = null;
  45. private $testAdapterStorage = null;
  46. private $asserterCallManager = null;
  47. private $mockControllerLinker = null;
  48. private $phpPath = null;
  49. private $testedClassName = null;
  50. private $testedClassPath = null;
  51. private $currentMethod = null;
  52. private $testNamespace = null;
  53. private $testMethodPrefix = null;
  54. private $classEngine = null;
  55. private $bootstrapFile = null;
  56. private $maxAsynchronousEngines = null;
  57. private $asynchronousEngines = 0;
  58. private $path = '';
  59. private $class = '';
  60. private $classNamespace = '';
  61. private $observers = null;
  62. private $tags = array();
  63. private $phpVersions = array();
  64. private $mandatoryExtensions = array();
  65. private $dataProviders = array();
  66. private $testMethods = array();
  67. private $runTestMethods = array();
  68. private $engines = array();
  69. private $methodEngines = array();
  70. private $methodsAreNotVoid = array();
  71. private $executeOnFailure = array();
  72. private $ignore = false;
  73. private $debugMode = false;
  74. private $xdebugConfig = null;
  75. private $codeCoverage = false;
  76. private $classHasNotVoidMethods = false;
  77. private $extensions = null;
  78. private static $namespace = null;
  79. private static $methodPrefix = null;
  80. private static $defaultEngine = self::defaultEngine;
  81. public function __construct(adapter $adapter = null, annotations\extractor $annotationExtractor = null, asserter\generator $asserterGenerator = null, test\assertion\manager $assertionManager = null, \closure $reflectionClassFactory = null)
  82. {
  83. $this
  84. ->setAdapter($adapter)
  85. ->setPhpMocker()
  86. ->setMockGenerator()
  87. ->setMockAutoloader()
  88. ->setAsserterGenerator($asserterGenerator)
  89. ->setAssertionManager($assertionManager)
  90. ->setTestAdapterStorage()
  91. ->setMockControllerLinker()
  92. ->setScore()
  93. ->setLocale()
  94. ->setFactoryBuilder()
  95. ->setReflectionMethodFactory()
  96. ->setAsserterCallManager()
  97. ->enableCodeCoverage()
  98. ;
  99. $this->observers = new \splObjectStorage();
  100. $this->extensions = new \splObjectStorage();
  101. $class = ($reflectionClassFactory ? $reflectionClassFactory($this) : new \reflectionClass($this));
  102. $this->path = $class->getFilename();
  103. $this->class = $class->getName();
  104. $this->classNamespace = $class->getNamespaceName();
  105. if ($annotationExtractor === null)
  106. {
  107. $annotationExtractor = new annotations\extractor();
  108. }
  109. $this->setClassAnnotations($annotationExtractor);
  110. $annotationExtractor->extract($class->getDocComment());
  111. if ($this->testNamespace === null || $this->testMethodPrefix === null)
  112. {
  113. $annotationExtractor
  114. ->unsetHandler('ignore')
  115. ->unsetHandler('tags')
  116. ->unsetHandler('maxChildrenNumber')
  117. ;
  118. $parentClass = $class;
  119. while (($this->testNamespace === null || $this->testMethodPrefix === null) && ($parentClass = $parentClass->getParentClass()) !== false)
  120. {
  121. $annotationExtractor->extract($parentClass->getDocComment());
  122. if ($this->testNamespace !== null)
  123. {
  124. $annotationExtractor->unsetHandler('namespace');
  125. }
  126. if ($this->testMethodPrefix !== null)
  127. {
  128. $annotationExtractor->unsetHandler('methodPrefix');
  129. }
  130. }
  131. }
  132. $this->setMethodAnnotations($annotationExtractor, $methodName);
  133. $testMethodPrefix = $this->getTestMethodPrefix();
  134. if (static::isRegex($testMethodPrefix) === false)
  135. {
  136. $testMethodFilter = function($methodName) use ($testMethodPrefix) { return (stripos($methodName, $testMethodPrefix) === 0); };
  137. }
  138. else
  139. {
  140. $testMethodFilter = function($methodName) use ($testMethodPrefix) { return (preg_match($testMethodPrefix, $methodName) == true); };
  141. }
  142. foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $publicMethod)
  143. {
  144. $methodName = $publicMethod->getName();
  145. if ($testMethodFilter($methodName) == true)
  146. {
  147. $this->testMethods[$methodName] = array();
  148. $annotationExtractor->extract($publicMethod->getDocComment());
  149. if ($publicMethod->getNumberOfParameters() > 0 && isset($this->dataProviders[$methodName]) === false)
  150. {
  151. $this->setDataProvider($methodName);
  152. }
  153. }
  154. }
  155. $this->runTestMethods($this->getTestMethods());
  156. }
  157. public function __toString()
  158. {
  159. return $this->getClass();
  160. }
  161. public function __get($property)
  162. {
  163. return $this->assertionManager->__get($property);
  164. }
  165. public function __set($property, $handler)
  166. {
  167. $this->assertionManager->{$property} = $handler;
  168. return $this;
  169. }
  170. public function __call($method, array $arguments)
  171. {
  172. return $this->assertionManager->__call($method, $arguments);
  173. }
  174. public function setTestAdapterStorage(test\adapter\storage $storage = null)
  175. {
  176. $this->testAdapterStorage = $storage ?: new test\adapter\storage();
  177. return $this;
  178. }
  179. public function getTestAdapterStorage()
  180. {
  181. return $this->testAdapterStorage;
  182. }
  183. public function setMockControllerLinker(mock\controller\linker $linker = null)
  184. {
  185. $this->mockControllerLinker = $linker ?: new mock\controller\linker();
  186. return $this;
  187. }
  188. public function getMockControllerLinker()
  189. {
  190. return $this->mockControllerLinker;
  191. }
  192. public function setScore(test\score $score = null)
  193. {
  194. $this->score = $score ?: new test\score();
  195. return $this;
  196. }
  197. public function getScore()
  198. {
  199. return $this->score;
  200. }
  201. public function setLocale(locale $locale = null)
  202. {
  203. $this->locale = $locale ?: new locale();
  204. return $this;
  205. }
  206. public function getLocale()
  207. {
  208. return $this->locale;
  209. }
  210. public function setAdapter(adapter $adapter = null)
  211. {
  212. $this->adapter = $adapter ?: new adapter();
  213. return $this;
  214. }
  215. public function getAdapter()
  216. {
  217. return $this->adapter;
  218. }
  219. public function setPhpMocker(php\mocker $phpMocker = null)
  220. {
  221. $this->phpMocker = $phpMocker ?: new php\mocker();
  222. return $this;
  223. }
  224. public function getPhpMocker()
  225. {
  226. return $this->phpMocker;
  227. }
  228. public function setMockGenerator(test\mock\generator $generator = null)
  229. {
  230. if ($generator !== null)
  231. {
  232. $generator->setTest($this);
  233. }
  234. else
  235. {
  236. $generator = new test\mock\generator($this);
  237. }
  238. $this->mockGenerator = $generator;
  239. return $this;
  240. }
  241. public function getMockGenerator()
  242. {
  243. return $this->mockGenerator;
  244. }
  245. public function setMockAutoloader(atoum\autoloader\mock $autoloader = null)
  246. {
  247. $this->mockAutoloader = $autoloader ?: new atoum\autoloader\mock();
  248. return $this;
  249. }
  250. public function getMockAutoloader()
  251. {
  252. return $this->mockAutoloader;
  253. }
  254. public function setFactoryBuilder(factory\builder $factoryBuilder = null)
  255. {
  256. $this->factoryBuilder = $factoryBuilder ?: new factory\builder\closure();
  257. return $this;
  258. }
  259. public function getFactoryBuilder()
  260. {
  261. return $this->factoryBuilder;
  262. }
  263. public function setReflectionMethodFactory(\closure $factory = null)
  264. {
  265. $this->reflectionMethodFactory = $factory ?: function($class, $method) { return new \reflectionMethod($class, $method); };
  266. return $this;
  267. }
  268. public function setAsserterGenerator(test\asserter\generator $generator = null)
  269. {
  270. if ($generator !== null)
  271. {
  272. $generator->setTest($this);
  273. }
  274. else
  275. {
  276. $generator = new test\asserter\generator($this);
  277. }
  278. $this->asserterGenerator = $generator->setTest($this);
  279. return $this;
  280. }
  281. public function getAsserterGenerator()
  282. {
  283. $this->testAdapterStorage->resetCalls();
  284. return $this->asserterGenerator;
  285. }
  286. public function setAssertionManager(test\assertion\manager $assertionManager = null)
  287. {
  288. $this->assertionManager = $assertionManager ?: new test\assertion\manager();
  289. $test = $this;
  290. $this->assertionManager
  291. ->setHandler('when', function($mixed) use ($test) { if ($mixed instanceof \closure) { $mixed($test); } return $test; })
  292. ->setHandler('assert', function($case = null) use ($test) { $test->stopCase(); if ($case !== null) { $test->startCase($case); } return $test; })
  293. ->setHandler('mockGenerator', function() use ($test) { return $test->getMockGenerator(); })
  294. ->setHandler('mockClass', function($class, $mockNamespace = null, $mockClass = null) use ($test) { $test->getMockGenerator()->generate($class, $mockNamespace, $mockClass); return $test; })
  295. ->setHandler('mockTestedClass', function($mockNamespace = null, $mockClass = null) use ($test) { $test->getMockGenerator()->generate($test->getTestedClassName(), $mockNamespace, $mockClass); return $test; })
  296. ->setHandler('dump', function() use ($test) { if ($test->debugModeIsEnabled() === true) { call_user_func_array('var_dump', func_get_args()); } return $test; })
  297. ->setHandler('stop', function() use ($test) { if ($test->debugModeIsEnabled() === true) { throw new test\exceptions\stop(); } return $test; })
  298. ->setHandler('executeOnFailure', function($callback) use ($test) { if ($test->debugModeIsEnabled() === true) { $test->executeOnFailure($callback); } return $test; })
  299. ->setHandler('dumpOnFailure', function($variable) use ($test) { if ($test->debugModeIsEnabled() === true) { $test->executeOnFailure(function() use ($variable) { var_dump($variable); }); } return $test; })
  300. ->setPropertyHandler('function', function() use ($test) { return $test->getPhpMocker(); })
  301. ->setPropertyHandler('exception', function() { return asserters\exception::getLastValue(); })
  302. ;
  303. $mockGenerator = $this->mockGenerator;
  304. $this->assertionManager
  305. ->setPropertyHandler('nextMockedMethod', function() use ($mockGenerator) { return $mockGenerator->getMethod(); })
  306. ;
  307. $returnTest = function() use ($test) { return $test; };
  308. $this->assertionManager
  309. ->setHandler('if', $returnTest)
  310. ->setHandler('and', $returnTest)
  311. ->setHandler('then', $returnTest)
  312. ->setHandler('given', $returnTest)
  313. ->setMethodHandler('define', $returnTest)
  314. ;
  315. $returnMockController = function(mock\aggregator $mock) { return $mock->getMockController(); };
  316. $this->assertionManager
  317. ->setHandler('calling', $returnMockController)
  318. ->setHandler('Æ’', $returnMockController)
  319. ;
  320. $this->assertionManager
  321. ->setHandler('resetMock', function(mock\aggregator $mock) { return $mock->getMockController()->resetCalls(); })
  322. ->setHandler('resetAdapter', function(test\adapter $adapter) { return $adapter->resetCalls(); })
  323. ;
  324. $phpMocker = $this->phpMocker;
  325. $this->assertionManager->setHandler('resetFunction', function(test\adapter\invoker $invoker) use ($phpMocker) { $phpMocker->resetCalls($invoker->getFunction()); return $invoker; });
  326. $assertionAliaser = $this->assertionManager->getAliaser();
  327. $this->assertionManager
  328. ->setPropertyHandler('define', function() use ($assertionAliaser, $test) { return $assertionAliaser; })
  329. ->setHandler('from', function($class) use ($assertionAliaser, $test) { $assertionAliaser->from($class); return $test; })
  330. ->setHandler('use', function($target) use ($assertionAliaser, $test) { $assertionAliaser->alias($target); return $test; })
  331. ->setHandler('as', function($alias) use ($assertionAliaser, $test) { $assertionAliaser->to($alias); return $test; })
  332. ;
  333. $asserterGenerator = $this->asserterGenerator;
  334. $this->assertionManager->setDefaultHandler(function($keyword, $arguments) use ($asserterGenerator, $assertionAliaser, & $lastAsserter) {
  335. static $lastAsserter = null;
  336. if ($lastAsserter !== null)
  337. {
  338. $realKeyword = $assertionAliaser->resolveAlias($keyword, get_class($lastAsserter));
  339. if ($realKeyword !== $keyword)
  340. {
  341. return call_user_func_array(array($lastAsserter, $realKeyword), $arguments);
  342. }
  343. }
  344. return ($lastAsserter = $asserterGenerator->getAsserterInstance($keyword, $arguments));
  345. }
  346. );
  347. $this->assertionManager
  348. ->use('phpArray')->as('array')
  349. ->use('phpArray')->as('in')
  350. ->use('phpClass')->as('class')
  351. ->use('phpFunction')->as('function')
  352. ->use('calling')->as('method')
  353. ;
  354. return $this;
  355. }
  356. public function getAsserterCallManager()
  357. {
  358. return $this->asserterCallManager;
  359. }
  360. public function setAsserterCallManager(asserters\adapter\call\manager $asserterCallManager = null)
  361. {
  362. $this->asserterCallManager = $asserterCallManager ?: new asserters\adapter\call\manager();
  363. return $this;
  364. }
  365. public function addClassPhpVersion($version, $operator = null)
  366. {
  367. $this->phpVersions[$version] = $operator ?: '>=';
  368. return $this;
  369. }
  370. public function getClassPhpVersions()
  371. {
  372. return $this->phpVersions;
  373. }
  374. public function addMandatoryClassExtension($extension)
  375. {
  376. $this->mandatoryExtensions[] = $extension;
  377. return $this;
  378. }
  379. public function addMethodPhpVersion($testMethodName, $version, $operator = null)
  380. {
  381. $this->checkMethod($testMethodName)->testMethods[$testMethodName]['php'][$version] = $operator ?: '>=';
  382. return $this;
  383. }
  384. public function getMethodPhpVersions($testMethodName = null)
  385. {
  386. $versions = array();
  387. $classVersions = $this->getClassPhpVersions();
  388. if ($testMethodName === null)
  389. {
  390. foreach ($this->testMethods as $testMethodName => $annotations)
  391. {
  392. if (isset($annotations['php']) === false)
  393. {
  394. $versions[$testMethodName] = $classVersions;
  395. }
  396. else
  397. {
  398. $versions[$testMethodName] = array_merge($classVersions, $annotations['php']);
  399. }
  400. }
  401. }
  402. else
  403. {
  404. if (isset($this->checkMethod($testMethodName)->testMethods[$testMethodName]['php']) === false)
  405. {
  406. $versions = $classVersions;
  407. }
  408. else
  409. {
  410. $versions = array_merge($classVersions, $this->testMethods[$testMethodName]['php']);
  411. }
  412. }
  413. return $versions;
  414. }
  415. public function getMandatoryClassExtensions()
  416. {
  417. return $this->mandatoryExtensions;
  418. }
  419. public function addMandatoryMethodExtension($testMethodName, $extension)
  420. {
  421. $this->checkMethod($testMethodName)->testMethods[$testMethodName]['mandatoryExtensions'][] = $extension;
  422. return $this;
  423. }
  424. public function getMandatoryMethodExtensions($testMethodName = null)
  425. {
  426. $extensions = array();
  427. $mandatoryClassExtensions = $this->getMandatoryClassExtensions();
  428. if ($testMethodName === null)
  429. {
  430. foreach ($this->testMethods as $testMethodName => $annotations)
  431. {
  432. if (isset($annotations['mandatoryExtensions']) === false)
  433. {
  434. $extensions[$testMethodName] = $mandatoryClassExtensions;
  435. }
  436. else
  437. {
  438. $extensions[$testMethodName] = array_merge($mandatoryClassExtensions, $annotations['mandatoryExtensions']);
  439. }
  440. }
  441. }
  442. else
  443. {
  444. if (isset($this->checkMethod($testMethodName)->testMethods[$testMethodName]['mandatoryExtensions']) === false)
  445. {
  446. $extensions = $mandatoryClassExtensions;
  447. }
  448. else
  449. {
  450. $extensions = array_merge($mandatoryClassExtensions, $this->testMethods[$testMethodName]['mandatoryExtensions']);
  451. }
  452. }
  453. return $extensions;
  454. }
  455. public function skip($message)
  456. {
  457. throw new test\exceptions\skip($message);
  458. }
  459. public function getAssertionManager()
  460. {
  461. return $this->assertionManager;
  462. }
  463. public function setClassEngine($engine)
  464. {
  465. $this->classEngine = (string) $engine;
  466. return $this;
  467. }
  468. public function getClassEngine()
  469. {
  470. return $this->classEngine;
  471. }
  472. public function classHasVoidMethods()
  473. {
  474. $this->classHasNotVoidMethods = false;
  475. }
  476. public function classHasNotVoidMethods()
  477. {
  478. $this->classHasNotVoidMethods = true;
  479. }
  480. public function setMethodVoid($method)
  481. {
  482. $this->methodsAreNotVoid[$method] = false;
  483. }
  484. public function setMethodNotVoid($method)
  485. {
  486. $this->methodsAreNotVoid[$method] = true;
  487. }
  488. public function methodIsNotVoid($method)
  489. {
  490. return (isset($this->methodsAreNotVoid[$method]) === false ? $this->classHasNotVoidMethods : $this->methodsAreNotVoid[$method]);
  491. }
  492. public function setMethodEngine($method, $engine)
  493. {
  494. $this->methodEngines[(string) $method] = (string) $engine;
  495. return $this;
  496. }
  497. public function getMethodEngine($method)
  498. {
  499. $method = (string) $method;
  500. return (isset($this->methodEngines[$method]) === false ? null : $this->methodEngines[$method]);
  501. }
  502. public function enableDebugMode()
  503. {
  504. $this->debugMode = true;
  505. return $this;
  506. }
  507. public function disableDebugMode()
  508. {
  509. $this->debugMode = false;
  510. return $this;
  511. }
  512. public function debugModeIsEnabled()
  513. {
  514. return $this->debugMode;
  515. }
  516. public function setXdebugConfig($value)
  517. {
  518. $this->xdebugConfig = $value;
  519. return $this;
  520. }
  521. public function getXdebugConfig()
  522. {
  523. return $this->xdebugConfig;
  524. }
  525. public function executeOnFailure(\closure $closure)
  526. {
  527. $this->executeOnFailure[] = $closure;
  528. return $this;
  529. }
  530. public function codeCoverageIsEnabled()
  531. {
  532. return $this->codeCoverage;
  533. }
  534. public function enableCodeCoverage()
  535. {
  536. $this->codeCoverage = $this->adapter->extension_loaded('xdebug');
  537. return $this;
  538. }
  539. public function disableCodeCoverage()
  540. {
  541. $this->codeCoverage = false;
  542. return $this;
  543. }
  544. public function setMaxChildrenNumber($number)
  545. {
  546. $number = (int) $number;
  547. if ($number < 1)
  548. {
  549. throw new exceptions\logic\invalidArgument('Maximum number of children must be greater or equal to 1');
  550. }
  551. $this->maxAsynchronousEngines = $number;
  552. return $this;
  553. }
  554. public function setBootstrapFile($path)
  555. {
  556. $this->bootstrapFile = $path;
  557. return $this;
  558. }
  559. public function getBootstrapFile()
  560. {
  561. return $this->bootstrapFile;
  562. }
  563. public function setTestNamespace($testNamespace)
  564. {
  565. $this->testNamespace = self::cleanNamespace($testNamespace);
  566. if ($this->testNamespace === '')
  567. {
  568. throw new exceptions\logic\invalidArgument('Test namespace must not be empty');
  569. }
  570. return $this;
  571. }
  572. public function getTestNamespace()
  573. {
  574. return $this->testNamespace ?: self::getNamespace();
  575. }
  576. public function setTestMethodPrefix($methodPrefix)
  577. {
  578. $methodPrefix = (string) $methodPrefix;
  579. if ($methodPrefix == '')
  580. {
  581. throw new exceptions\logic\invalidArgument('Test method prefix must not be empty');
  582. }
  583. $this->testMethodPrefix = $methodPrefix;
  584. return $this;
  585. }
  586. public function getTestMethodPrefix()
  587. {
  588. return $this->testMethodPrefix ?: self::getMethodPrefix();
  589. }
  590. public function setPhpPath($path)
  591. {
  592. $this->phpPath = (string) $path;
  593. return $this;
  594. }
  595. public function getPhpPath()
  596. {
  597. return $this->phpPath;
  598. }
  599. public function getAllTags()
  600. {
  601. $tags = $this->getTags();
  602. foreach ($this->testMethods as $annotations)
  603. {
  604. if (isset($annotations['tags']) === true)
  605. {
  606. $tags = array_merge($tags, array_diff($annotations['tags'], $tags));
  607. }
  608. }
  609. return array_values($tags);
  610. }
  611. public function setTags(array $tags)
  612. {
  613. $this->tags = $tags;
  614. return $this;
  615. }
  616. public function getTags()
  617. {
  618. return $this->tags;
  619. }
  620. public function setMethodTags($testMethodName, array $tags)
  621. {
  622. $this->checkMethod($testMethodName)->testMethods[$testMethodName]['tags'] = $tags;
  623. return $this;
  624. }
  625. public function getMethodTags($testMethodName = null)
  626. {
  627. $tags = array();
  628. $classTags = $this->getTags();
  629. if ($testMethodName === null)
  630. {
  631. foreach ($this->testMethods as $testMethodName => $annotations)
  632. {
  633. $tags[$testMethodName] = isset($annotations['tags']) === false ? $classTags : $annotations['tags'];
  634. }
  635. }
  636. else
  637. {
  638. $tags = isset($this->checkMethod($testMethodName)->testMethods[$testMethodName]['tags']) === false ? $classTags : $this->testMethods[$testMethodName]['tags'];
  639. }
  640. return $tags;
  641. }
  642. public function getDataProviders()
  643. {
  644. return $this->dataProviders;
  645. }
  646. public function getTestedClassName()
  647. {
  648. if ($this->testedClassName === null)
  649. {
  650. $this->testedClassName = self::getTestedClassNameFromTestClass($this->getClass(), $this->getTestNamespace());
  651. }
  652. return $this->testedClassName;
  653. }
  654. public function getTestedClassNamespace()
  655. {
  656. $testedClassName = $this->getTestedClassName();
  657. return substr($testedClassName, 0, strrpos($testedClassName, '\\'));
  658. }
  659. public function getTestedClassPath()
  660. {
  661. if ($this->testedClassPath === null)
  662. {
  663. $testedClass = new \reflectionClass($this->getTestedClassName());
  664. $this->testedClassPath = $testedClass->getFilename();
  665. }
  666. return $this->testedClassPath;
  667. }
  668. public function setTestedClassName($className)
  669. {
  670. if ($this->testedClassName !== null)
  671. {
  672. throw new exceptions\runtime('Tested class name is already defined');
  673. }
  674. $this->testedClassName = $className;
  675. return $this;
  676. }
  677. public function getClass()
  678. {
  679. return $this->class;
  680. }
  681. public function getClassNamespace()
  682. {
  683. return $this->classNamespace;
  684. }
  685. public function getPath()
  686. {
  687. return $this->path;
  688. }
  689. public function getTaggedTestMethods(array $methods, array $tags = array())
  690. {
  691. return array_values(array_uintersect($methods, $this->getTestMethods($tags), 'strcasecmp'));
  692. }
  693. public function getTestMethods(array $tags = array())
  694. {
  695. $testMethods = array();
  696. foreach (array_keys($this->testMethods) as $methodName)
  697. {
  698. if ($this->methodIsIgnored($methodName, $tags) === false)
  699. {
  700. $testMethods[] = $methodName;
  701. }
  702. }
  703. return $testMethods;
  704. }
  705. public function getCurrentMethod()
  706. {
  707. return $this->currentMethod;
  708. }
  709. public function getMaxChildrenNumber()
  710. {
  711. return $this->maxAsynchronousEngines;
  712. }
  713. public function getCoverage()
  714. {
  715. return $this->score->getCoverage();
  716. }
  717. public function count()
  718. {
  719. return sizeof($this->runTestMethods);
  720. }
  721. public function addObserver(observer $observer)
  722. {
  723. $this->observers->attach($observer);
  724. return $this;
  725. }
  726. public function removeObserver(atoum\observer $observer)
  727. {
  728. $this->observers->detach($observer);
  729. return $this;
  730. }
  731. public function getObservers()
  732. {
  733. return iterator_to_array($this->observers);
  734. }
  735. public function callObservers($event)
  736. {
  737. foreach ($this->observers as $observer)
  738. {
  739. $observer->handleEvent($event, $this);
  740. }
  741. return $this;
  742. }
  743. public function ignore($boolean)
  744. {
  745. $this->ignore = ($boolean == true);
  746. return $this->runTestMethods($this->getTestMethods());
  747. }
  748. public function isIgnored(array $namespaces = array(), array $tags = array())
  749. {
  750. $isIgnored = (sizeof($this) <= 0 || $this->ignore === true);
  751. if ($isIgnored === false && sizeof($namespaces) > 0)
  752. {
  753. $classNamespace = strtolower($this->getClassNamespace());
  754. $isIgnored = sizeof(array_filter($namespaces, function($value) use ($classNamespace) { return strpos($classNamespace, strtolower($value)) === 0; })) <= 0;
  755. }
  756. if ($isIgnored === false && sizeof($tags) > 0)
  757. {
  758. $isIgnored = sizeof($testTags = $this->getAllTags()) <= 0 || sizeof(array_intersect($tags, $testTags)) == 0;
  759. }
  760. return $isIgnored;
  761. }
  762. public function ignoreMethod($methodName, $boolean)
  763. {
  764. $this->checkMethod($methodName)->testMethods[$methodName]['ignore'] = $boolean == true;
  765. return $this->runTestMethods($this->getTestMethods());
  766. }
  767. public function methodIsIgnored($methodName, array $tags = array())
  768. {
  769. $isIgnored = $this->checkMethod($methodName)->ignore;
  770. if ($isIgnored === false)
  771. {
  772. if (isset($this->testMethods[$methodName]['ignore']) === true)
  773. {
  774. $isIgnored = $this->testMethods[$methodName]['ignore'];
  775. }
  776. if ($isIgnored === false && $tags)
  777. {
  778. $isIgnored = sizeof($methodTags = $this->getMethodTags($methodName)) <= 0 || sizeof(array_intersect($tags, $methodTags)) <= 0;
  779. }
  780. }
  781. return $isIgnored;
  782. }
  783. public function runTestMethods(array $methods, array $tags = array())
  784. {
  785. $this->runTestMethods = $runTestMethods = array();
  786. if (isset($methods['*']) === true)
  787. {
  788. $runTestMethods = $methods['*'];
  789. }
  790. $testClass = $this->getClass();
  791. if (isset($methods[$testClass]) === true)
  792. {
  793. $runTestMethods = $methods[$testClass];
  794. }
  795. if (in_array('*', $runTestMethods) === true)
  796. {
  797. $runTestMethods = array();
  798. }
  799. if (sizeof($runTestMethods) <= 0)
  800. {
  801. $runTestMethods = $this->getTestMethods($tags);
  802. }
  803. else
  804. {
  805. $runTestMethods = $this->getTaggedTestMethods($runTestMethods, $tags);
  806. }
  807. foreach ($runTestMethods as $method)
  808. {
  809. if ($this->xdebugConfig != null)
  810. {
  811. $engineClass = 'mageekguy\atoum\test\engines\concurrent';
  812. }
  813. else
  814. {
  815. $engineName = $engineClass = ($this->getMethodEngine($method) ?: $this->getClassEngine() ?: self::getDefaultEngine());
  816. if (substr($engineClass, 0, 1) !== '\\')
  817. {
  818. $engineClass = self::enginesNamespace . '\\' . $engineClass;
  819. }
  820. if (class_exists($engineClass) === false)
  821. {
  822. throw new exceptions\runtime('Test engine \'' . $engineName . '\' does not exist for method \'' . $this->class . '::' . $method . '()\'');
  823. }
  824. }
  825. $engine = new $engineClass();
  826. if ($engine instanceof test\engine === false)
  827. {
  828. throw new exceptions\runtime('Test engine \'' . $engineName . '\' is invalid for method \'' . $this->class . '::' . $method . '()\'');
  829. }
  830. $this->runTestMethods[$method] = $engine;
  831. }
  832. return $this;
  833. }
  834. public function runTestMethod($testMethod, array $tags = array())
  835. {
  836. if ($this->methodIsIgnored($testMethod, $tags) === false)
  837. {
  838. $this->mockAutoloader->setMockGenerator($this->mockGenerator)->register();
  839. set_error_handler(array($this, 'errorHandler'));
  840. ini_set('display_errors', 'stderr');
  841. ini_set('log_errors', 'Off');
  842. ini_set('log_errors_max_len', '0');
  843. $this->currentMethod = $testMethod;
  844. $this->executeOnFailure = array();
  845. $this->phpMocker->setDefaultNamespace($this->getTestedClassNamespace());
  846. try
  847. {
  848. foreach ($this->getMethodPhpVersions($testMethod) as $phpVersion => $operator)
  849. {
  850. if (version_compare(phpversion(), $phpVersion, $operator) === false)
  851. {
  852. throw new test\exceptions\skip('PHP version ' . PHP_VERSION . ' is not ' . $operator . ' to ' . $phpVersion);
  853. }
  854. }
  855. foreach ($this->getMandatoryMethodExtensions($testMethod) as $mandatoryExtension)
  856. {
  857. $this->extension($mandatoryExtension)->isLoaded();
  858. }
  859. try
  860. {
  861. ob_start();
  862. test\adapter::setStorage($this->testAdapterStorage);
  863. mock\controller::setLinker($this->mockControllerLinker);
  864. $this->testAdapterStorage->add(php\mocker::getAdapter());
  865. $this->beforeTestMethod($this->currentMethod);
  866. $this->mockGenerator->testedClassIs($this->getTestedClassName());
  867. try
  868. {
  869. $testedClass = new \reflectionClass($testedClassName = $this->getTestedClassName());
  870. }
  871. catch (\exception $exception)
  872. {
  873. throw new exceptions\runtime('Tested class \'' . $testedClassName . '\' does not exist for test class \'' . $this->getClass() . '\'');
  874. }
  875. if ($testedClass->isAbstract() === true)
  876. {
  877. $testedClass = new \reflectionClass($testedClassName = $this->mockGenerator->getDefaultNamespace() . '\\' . $testedClassName);
  878. }
  879. $this->factoryBuilder->build($testedClass, $instance)
  880. ->addToAssertionManager($this->assertionManager, 'newTestedInstance', function() use ($testedClass) {
  881. throw new exceptions\runtime('Tested class ' . $testedClass->getName() . ' has no constructor or its constructor has at least one mandatory argument');
  882. }
  883. )
  884. ;
  885. $this->factoryBuilder->build($testedClass)
  886. ->addToAssertionManager($this->assertionManager, 'newInstance', function() use ($testedClass) {
  887. throw new exceptions\runtime('Tested class ' . $testedClass->getName() . ' has no constructor or its constructor has at least one mandatory argument');
  888. }
  889. )
  890. ;
  891. $this->assertionManager->setPropertyHandler('testedInstance', function() use (& $instance) {
  892. if ($instance === null)
  893. {
  894. throw new exceptions\runtime('Use $this->newTestedInstance before using $this->testedInstance');
  895. }
  896. return $instance;
  897. }
  898. );
  899. if ($this->codeCoverageIsEnabled() === true)
  900. {
  901. xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
  902. }
  903. $assertionNumber = $this->score->getAssertionNumber();
  904. $time = microtime(true);
  905. $memory = memory_get_usage(true);
  906. if (isset($this->dataProviders[$testMethod]) === false)
  907. {
  908. $this->{$testMethod}();
  909. $this->asserterCallManager->check();
  910. }
  911. else
  912. {
  913. $data = $this->{$this->dataProviders[$testMethod]}();
  914. if (is_array($data) === false && $data instanceof \traversable === false)
  915. {
  916. throw new test\exceptions\runtime('Data provider ' . $this->getClass() . '::' . $this->dataProviders[$testMethod] . '() must return an array or an iterator');
  917. }
  918. $reflectedTestMethod = call_user_func($this->reflectionMethodFactory, $this, $testMethod);
  919. $numberOfArguments = $reflectedTestMethod->getNumberOfRequiredParameters();
  920. foreach ($data as $key => $arguments)
  921. {
  922. if (is_array($arguments) === false)
  923. {
  924. $arguments = array($arguments);
  925. }
  926. if (sizeof($arguments) != $numberOfArguments)
  927. {
  928. throw new test\exceptions\runtime('Data provider ' . $this->getClass() . '::' . $this->dataProviders[$testMethod] . '() not provide enough arguments at key ' . $key . ' for test method ' . $this->getClass() . '::' . $testMethod . '()');
  929. }
  930. $this->score->setDataSet($key, $this->dataProviders[$testMethod]);
  931. $reflectedTestMethod->invokeArgs($this, $arguments);
  932. $this->asserterCallManager->check();
  933. $this->score->unsetDataSet();
  934. }
  935. }
  936. $this->mockControllerLinker->reset();
  937. $this->testAdapterStorage->reset();
  938. $memoryUsage = memory_get_usage(true) - $memory;
  939. $duration = microtime(true) - $time;
  940. $this->score
  941. ->addMemoryUsage($this->path, $this->class, $this->currentMethod, $memoryUsage)
  942. ->addDuration($this->path, $this->class, $this->currentMethod, $duration)
  943. ->addOutput($this->path, $this->class, $this->currentMethod, ob_get_clean())
  944. ;
  945. if ($this->codeCoverageIsEnabled() === true)
  946. {
  947. $this->score->getCoverage()->addXdebugDataForTest($this, xdebug_get_code_coverage());
  948. xdebug_stop_code_coverage();
  949. }
  950. if ($assertionNumber == $this->score->getAssertionNumber() && $this->methodIsNotVoid($this->currentMethod) === false)
  951. {
  952. $this->score->addVoidMethod($this->path, $this->class, $this->currentMethod);
  953. }
  954. }
  955. catch (\exception $exception)
  956. {
  957. $this->score->addOutput($this->path, $this->class, $this->currentMethod, ob_get_clean());
  958. throw $exception;
  959. }
  960. }
  961. catch (asserter\exception $exception)
  962. {
  963. foreach ($this->executeOnFailure as $closure)
  964. {
  965. ob_start();
  966. $closure();
  967. $this->score->addOutput($this->path, $this->class, $this->currentMethod, ob_get_clean());
  968. }
  969. if ($this->score->failExists($exception) === false)
  970. {
  971. $this->addExceptionToScore($exception);
  972. }
  973. }
  974. catch (test\exceptions\runtime $exception)
  975. {
  976. $this->score->addRuntimeException($this->path, $this->class, $this->currentMethod, $exception);
  977. }
  978. catch (test\exceptions\skip $exception)
  979. {
  980. list($file, $line) = $this->getBacktrace($exception->getTrace());
  981. $this->score->addSkippedMethod($file, $this->class, $this->currentMethod, $line, $exception->getMessage());
  982. }
  983. catch (test\exceptions\stop $exception)
  984. {
  985. }
  986. catch (exception $exception)
  987. {
  988. list($file, $line) = $this->getBacktrace($exception->getTrace());
  989. $this->errorHandler(E_USER_ERROR, $exception->getMessage(), $file, $line);
  990. }
  991. catch (\exception $exception)
  992. {
  993. $this->addExceptionToScore($exception);
  994. }
  995. $this->afterTestMethod($this->currentMethod);
  996. $this->currentMethod = null;
  997. restore_error_handler();
  998. ini_restore('display_errors');
  999. ini_restore('log_errors');
  1000. ini_restore('log_errors_max_len');
  1001. $this->mockAutoloader->unregister();
  1002. }
  1003. return $this;
  1004. }
  1005. public function run(array $runTestMethods = array(), array $tags = array())
  1006. {
  1007. if ($runTestMethods)
  1008. {
  1009. $this->runTestMethods(array_intersect($runTestMethods, $this->getTestMethods($tags)));
  1010. }
  1011. if ($this->isIgnored() === false)
  1012. {
  1013. $this->callObservers(self::runStart);
  1014. try
  1015. {
  1016. $this->runEngines();
  1017. }
  1018. catch (\exception $exception)
  1019. {
  1020. $this->stopEngines();
  1021. throw $exception;
  1022. }
  1023. $this->callObservers(self::runStop);
  1024. }
  1025. return $this;
  1026. }
  1027. public function startCase($case)
  1028. {
  1029. $this->testAdapterStorage->resetCalls();
  1030. $this->score->setCase($case);
  1031. return $this;
  1032. }
  1033. public function stopCase()
  1034. {
  1035. $this->testAdapterStorage->resetCalls();
  1036. $this->score->unsetCase();
  1037. return $this;
  1038. }
  1039. public function setDataProvider($testMethodName, $dataProvider = null)
  1040. {
  1041. if ($dataProvider === null)
  1042. {
  1043. $dataProvider = $testMethodName . 'DataProvider';
  1044. }
  1045. if (method_exists($this->checkMethod($testMethodName), $dataProvider) === false)
  1046. {
  1047. throw new exceptions\logic\invalidArgument('Data provider ' . $this->class . '::' . lcfirst($dataProvider) . '() is unknown');
  1048. }
  1049. $this->dataProviders[$testMethodName] = $dataProvider;
  1050. return $this;
  1051. }
  1052. public function errorHandler($errno, $errstr, $errfile, $errline)
  1053. {
  1054. $doNotCallDefaultErrorHandler = true;
  1055. $errorReporting = $this->adapter->error_reporting();
  1056. if ($errorReporting !== 0 && $errorReporting & $errno)
  1057. {
  1058. list($file, $line) = $this->getBacktrace();
  1059. $this->score->addError($file ?: ($errfile ?: $this->path), $this->class, $this->currentMethod, $line ?: $errline, $errno, trim($errstr), $errfile, $errline);
  1060. $doNotCallDefaultErrorHandler = !($errno & E_RECOVERABLE_ERROR);
  1061. }
  1062. return $doNotCallDefaultErrorHandler;
  1063. }
  1064. public function setUp() {}
  1065. public function beforeTestMethod($testMethod) {}
  1066. public function afterTestMethod($testMethod) {}
  1067. public function tearDown() {}
  1068. public static function setNamespace($namespace)
  1069. {
  1070. $namespace = self::cleanNamespace($namespace);
  1071. if ($namespace === '')
  1072. {
  1073. throw new exceptions\logic\invalidArgument('Namespace must not be empty');
  1074. }
  1075. self::$namespace = $namespace;
  1076. }
  1077. public static function getNamespace()
  1078. {
  1079. return self::$namespace ?: static::defaultNamespace;
  1080. }
  1081. public static function setMethodPrefix($methodPrefix)
  1082. {
  1083. if ($methodPrefix == '')
  1084. {
  1085. throw new exceptions\logic\invalidArgument('Method prefix must not be empty');
  1086. }
  1087. self::$methodPrefix = $methodPrefix;
  1088. }
  1089. public static function getMethodPrefix()
  1090. {
  1091. return self::$methodPrefix ?: static::defaultMethodPrefix;
  1092. }
  1093. public static function setDefaultEngine($defaultEngine)
  1094. {
  1095. self::$defaultEngine = (string) $defaultEngine;
  1096. }
  1097. public static function getDefaultEngine()
  1098. {
  1099. return self::$defaultEngine ?: self::defaultEngine;
  1100. }
  1101. public static function getTestedClassNameFromTestClass($fullyQualifiedClassName, $testNamespace = null)
  1102. {
  1103. if ($testNamespace === null)
  1104. {
  1105. $testNamespace = self::getNamespace();
  1106. }
  1107. if (self::isRegex($testNamespace) === true)
  1108. {
  1109. if (preg_match($testNamespace, $fullyQualifiedClassName) === 0)
  1110. {
  1111. throw new exceptions\runtime('Test class \'' . $fullyQualifiedClassName . '\' is not in a namespace which match pattern \'' . $testNamespace . '\'');
  1112. }
  1113. $testedClassName = preg_replace($testNamespace, '\\', $fullyQualifiedClassName);
  1114. }
  1115. else
  1116. {
  1117. $position = strpos($fullyQualifiedClassName, $testNamespace);
  1118. if ($position === false)
  1119. {
  1120. throw new exceptions\runtime('Test class \'' . $fullyQualifiedClassName . '\' is not in a namespace which contains \'' . $testNamespace . '\'');
  1121. }
  1122. $testedClassName = substr($fullyQualifiedClassName, 0, $position) . substr($fullyQualifiedClassName, $position + 1 + strlen($testNamespace));
  1123. }
  1124. return trim($testedClassName, '\\');
  1125. }
  1126. protected function setClassAnnotations(annotations\extractor $extractor)
  1127. {
  1128. $test = $this;
  1129. $extractor
  1130. ->resetHandlers()
  1131. ->setHandler('ignore', function($value) use ($test) { $test->ignore(annotations\extractor::toBoolean($value)); })
  1132. ->setHandler('tags', function($value) use ($test) { $test->setTags(annotations\extractor::toArray($value)); })
  1133. ->setHandler('namespace', function($value) use ($test) { $test->setTestNamespace($value === true ? static::defaultNamespace : $value); })
  1134. ->setHandler('methodPrefix', function($value) use ($test) { $test->setTestMethodPrefix($value === true ? static::defaultMethodPrefix : $value); })
  1135. ->setHandler('maxChildrenNumber', function($value) use ($test) { $test->setMaxChildrenNumber($value); })
  1136. ->setHandler('engine', function($value) use ($test) { $test->setClassEngine($value); })
  1137. ->setHandler('hasVoidMethods', function($value) use ($test) { $test->classHasVoidMethods(); })
  1138. ->setHandler('hasNotVoidMethods', function($value) use ($test) { $test->classHasNotVoidMethods(); })
  1139. ->setHandler('php', function($value) use ($test) {
  1140. $value = annotations\extractor::toArray($value);
  1141. if (isset($value[0]) === true)
  1142. {
  1143. $operator = null;
  1144. if (isset($value[1]) === false)
  1145. {
  1146. $version = $value[0];
  1147. }
  1148. else
  1149. {
  1150. $version = $value[1];
  1151. switch ($value[0])
  1152. {
  1153. case '<':
  1154. case '<=':
  1155. case '=':
  1156. case '==':
  1157. case '>=':
  1158. case '>':
  1159. $operator = $value[0];
  1160. }
  1161. }
  1162. $test->addClassPhpVersion($version, $operator);
  1163. }
  1164. }
  1165. )
  1166. ->setHandler('extensions', function($value) use ($test) {
  1167. foreach (annotations\extractor::toArray($value) as $mandatoryExtension)
  1168. {
  1169. $test->addMandatoryClassExtension($mandatoryExtension);
  1170. }
  1171. }
  1172. )
  1173. ;
  1174. return $this;
  1175. }
  1176. protected function setMethodAnnotations(annotations\extractor $extractor, & $methodName)
  1177. {
  1178. $test = $this;
  1179. $extractor
  1180. ->resetHandlers()
  1181. ->setHandler('ignore', function($value) use ($test, & $methodName) { $test->ignoreMethod($methodName, annotations\extractor::toBoolean($value)); })
  1182. ->setHandler('tags', function($value) use ($test, & $methodName) { $test->setMethodTags($methodName, annotations\extractor::toArray($value)); })
  1183. ->setHandler('dataProvider', function($value) use ($test, & $methodName) { $test->setDataProvider($methodName, $value === true ? null : $value); })
  1184. ->setHandler('engine', function($value) use ($test, & $methodName) { $test->setMethodEngine($methodName, $value); })
  1185. ->setHandler('isVoid', function($value) use ($test, & $methodName) { $test->setMethodVoid($methodName); })
  1186. ->setHandler('isNotVoid', function($value) use ($test, & $methodName) { $test->setMethodNotVoid($methodName); })
  1187. ->setHandler('php', function($value) use ($test, & $methodName) {
  1188. $value = annotations\extractor::toArray($value);
  1189. if (isset($value[0]) === true)
  1190. {
  1191. $operator = null;
  1192. if (isset($value[1]) === false)
  1193. {
  1194. $version = $value[0];
  1195. }
  1196. else
  1197. {
  1198. $version = $value[1];
  1199. switch ($value[0])
  1200. {
  1201. case '<':
  1202. case '<=':
  1203. case '=':
  1204. case '==':
  1205. case '>=':
  1206. case '>':
  1207. $operator = $value[0];
  1208. }
  1209. }
  1210. $test->addMethodPhpVersion($methodName, $version, $operator);
  1211. }
  1212. }
  1213. )
  1214. ->setHandler('extensions', function($value) use ($test, & $methodName) {
  1215. foreach (annotations\extractor::toArray($value) as $mandatoryExtension)
  1216. {
  1217. $test->addMandatoryMethodExtension($methodName, $mandatoryExtension);
  1218. }
  1219. }
  1220. )
  1221. ;
  1222. return $this;
  1223. }
  1224. protected function getBacktrace(array $trace = null)
  1225. {
  1226. $debugBacktrace = $trace === null ? debug_backtrace(false) : $trace;
  1227. foreach ($debugBacktrace as $key => $value)
  1228. {
  1229. if (isset($value['class']) === true && $value['class'] === $this->class && isset($value['function']) === true && $value['function'] === $this->currentMethod)
  1230. {
  1231. if (isset($debugBacktrace[$key - 1]) === true)
  1232. {
  1233. $key -= 1;
  1234. }
  1235. return array(
  1236. $debugBacktrace[$key]['file'],
  1237. $debugBacktrace[$key]['line']
  1238. );
  1239. }
  1240. }
  1241. return null;
  1242. }
  1243. private function checkMethod($methodName)
  1244. {
  1245. if (isset($this->testMethods[$methodName]) === false)
  1246. {
  1247. throw new exceptions\logic\invalidArgument('Test method ' . $this->class . '::' . $methodName . '() does not exist');
  1248. }
  1249. return $this;
  1250. }
  1251. private function addExceptionToScore(\exception $exception)
  1252. {
  1253. list($file, $line) = $this->getBacktrace($exception->getTrace());
  1254. $this->score->addException($file, $this->class, $this->currentMethod, $line, $exception);
  1255. return $this;
  1256. }
  1257. private function runEngines()
  1258. {
  1259. $this->callObservers(self::beforeSetUp);
  1260. $this->setUp();
  1261. $this->callObservers(self::afterSetUp);
  1262. while ($this->runEngine()->engines)
  1263. {
  1264. $engines = $this->engines;
  1265. foreach ($engines as $this->currentMethod => $engine)
  1266. {
  1267. $score = $engine->getScore();
  1268. if ($score !== null)
  1269. {
  1270. unset($this->engines[$this->currentMethod]);
  1271. $this
  1272. ->callObservers(self::afterTestMethod)
  1273. ->score
  1274. ->merge($score)
  1275. ;
  1276. $runtimeExceptions = $score->getRuntimeExceptions();
  1277. if (sizeof($runtimeExceptions) > 0)
  1278. {
  1279. $this->callObservers(self::runtimeException);
  1280. throw reset($runtimeExceptions);
  1281. }
  1282. else
  1283. {
  1284. switch (true)
  1285. {
  1286. case $score->getVoidMethodNumber():
  1287. $signal = self::void;
  1288. break;
  1289. case $score->getUncompletedMethodNumber():
  1290. $signal = self::uncompleted;
  1291. break;
  1292. case $score->getSkippedMethodNumber():
  1293. $signal = self::skipped;
  1294. break;
  1295. case $score->getFailNumber():
  1296. $signal = self::fail;
  1297. break;
  1298. case $score->getErrorNumber():
  1299. $signal = self::error;
  1300. break;
  1301. case $score->getExceptionNumber():
  1302. $signal = self::exception;
  1303. break;
  1304. default:
  1305. $signal = self::success;
  1306. }
  1307. $this->callObservers($signal);
  1308. }
  1309. if ($engine->isAsynchronous() === true)
  1310. {
  1311. $this->asynchronousEngines--;
  1312. }
  1313. }
  1314. }
  1315. $this->currentMethod = null;
  1316. }
  1317. return $this->doTearDown();
  1318. }
  1319. private function stopEngines()
  1320. {
  1321. while ($this->engines)
  1322. {
  1323. $engines = $this->engines;
  1324. foreach ($engines as $currentMethod => $engine)
  1325. {
  1326. if ($engine->getScore() !== null)
  1327. {
  1328. unset($this->engines[$currentMethod]);
  1329. }
  1330. }
  1331. }
  1332. return $this->doTearDown();
  1333. }
  1334. private function runEngine()
  1335. {
  1336. $engine = reset($this->runTestMethods);
  1337. if ($engine !== false)
  1338. {
  1339. $this->currentMethod = key($this->runTestMethods);
  1340. if ($this->canRunEngine($engine) === true)
  1341. {
  1342. unset($this->runTestMethods[$this->currentMethod]);
  1343. $this->engines[$this->currentMethod] = $engine->run($this->callObservers(self::beforeTestMethod));
  1344. if ($engine->isAsynchronous() === true)
  1345. {
  1346. $this->asynchronousEngines++;
  1347. }
  1348. }
  1349. $this->currentMethod = null;
  1350. }
  1351. return $this;
  1352. }
  1353. private function canRunEngine(test\engine $engine)
  1354. {
  1355. return ($engine->isAsynchronous() === false || $this->maxAsynchronousEngines === null || $this->asynchronousEngines < $this->maxAsynchronousEngines);
  1356. }
  1357. private function doTearDown()
  1358. {
  1359. $this->callObservers(self::beforeTearDown);
  1360. $this->tearDown();
  1361. $this->callObservers(self::afterTearDown);
  1362. return $this;
  1363. }
  1364. public function getExtensions()
  1365. {
  1366. return iterator_to_array($this->extensions);
  1367. }
  1368. public function removeExtension(atoum\extension $extension)
  1369. {
  1370. $this->extensions->detach($extension);
  1371. return $this->removeObserver($extension);;
  1372. }
  1373. public function removeExtensions()
  1374. {
  1375. foreach ($this->extensions as $extension)
  1376. {
  1377. $this->removeObserver($extension);
  1378. }
  1379. $this->extensions = new \splObjectStorage();
  1380. return $this;
  1381. }
  1382. public function addExtension(atoum\extension $extension)
  1383. {
  1384. if ($this->extensions->contains($extension) === false)
  1385. {
  1386. $extension->setTest($this);
  1387. $this->extensions->attach($extension);
  1388. $this->addObserver($extension);
  1389. }
  1390. return $this;
  1391. }
  1392. public function addExtensions(\traversable $extensions)
  1393. {
  1394. foreach ($extensions as $extension)
  1395. {
  1396. $this->addExtension($extension);
  1397. }
  1398. return $this;
  1399. }
  1400. private static function cleanNamespace($namespace)
  1401. {
  1402. return trim((string) $namespace, '\\');
  1403. }
  1404. private static function isRegex($namespace)
  1405. {
  1406. return preg_match('/^([^\\\[:alnum:][:space:]]).*\1.*$/', $namespace) === 1;
  1407. }
  1408. }