PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/asserters/adapter/call.php

https://github.com/agallou/atoum
PHP | 483 lines | 385 code | 98 blank | 0 comment | 37 complexity | 1afb9d99227f2738314c6454b719da93 MD5 | raw file
  1. <?php
  2. namespace mageekguy\atoum\asserters\adapter;
  3. use
  4. mageekguy\atoum,
  5. mageekguy\atoum\php,
  6. mageekguy\atoum\test,
  7. mageekguy\atoum\asserter,
  8. mageekguy\atoum\tools\variable,
  9. mageekguy\atoum\asserters\adapter\call\exceptions
  10. ;
  11. abstract class call extends atoum\asserter
  12. {
  13. protected $adapter = null;
  14. protected $call = null;
  15. protected $identicalCall = false;
  16. protected $beforeCalls = array();
  17. protected $afterCalls = array();
  18. protected $trace = array('file' => null, 'line' => null);
  19. protected $manager = null;
  20. public function __construct(asserter\generator $generator = null, variable\analyzer $analyzer = null, atoum\locale $locale = null)
  21. {
  22. parent::__construct($generator, $analyzer, $locale);
  23. $this->setCall();
  24. }
  25. public function __get($property)
  26. {
  27. if (is_numeric($property) === true)
  28. {
  29. return $this->exactly($property);
  30. }
  31. else switch (strtolower($property))
  32. {
  33. case 'once':
  34. case 'twice':
  35. case 'thrice':
  36. case 'never':
  37. case 'atleastonce':
  38. case 'wascalled':
  39. case 'wasnotcalled':
  40. return $this->{$property}();
  41. default:
  42. return parent::__get($property);
  43. }
  44. }
  45. public function setManager(call\manager $manager)
  46. {
  47. $this->manager = $manager;
  48. return $this;
  49. }
  50. public function setCall(test\adapter\call $call = null)
  51. {
  52. if ($call === null)
  53. {
  54. $call = new test\adapter\call();
  55. }
  56. if ($this->call !== null)
  57. {
  58. $call->copy($this->call);
  59. }
  60. $this->call = $call;
  61. return $this;
  62. }
  63. public function getCall()
  64. {
  65. return clone $this->call;
  66. }
  67. public function disableEvaluationChecking()
  68. {
  69. return $this->removeFromManager();
  70. }
  71. public function getLastAssertionFile()
  72. {
  73. return $this->trace['file'];
  74. }
  75. public function getLastAssertionLine()
  76. {
  77. return $this->trace['line'];
  78. }
  79. public function reset()
  80. {
  81. if ($this->adapter !== null)
  82. {
  83. $this->adapter->resetCalls();
  84. }
  85. return $this;
  86. }
  87. public function setWithTest(test $test)
  88. {
  89. $this->setManager($test->getAsserterCallManager());
  90. return parent::setWithTest($test);
  91. }
  92. public function setWith($adapter)
  93. {
  94. $this->adapter = $adapter;
  95. if ($this->adapter instanceof \mageekguy\atoum\test\adapter)
  96. {
  97. $this->pass();
  98. }
  99. else
  100. {
  101. $this->fail($this->_('%s is not a test adapter', $this->getTypeOf($this->adapter)));
  102. }
  103. return $this;
  104. }
  105. public function getAdapter()
  106. {
  107. return $this->adapter;
  108. }
  109. public function before(call $call)
  110. {
  111. $this->setTrace();
  112. foreach (func_get_args() as $call)
  113. {
  114. $this->addBeforeCall($call);
  115. }
  116. return $this;
  117. }
  118. public function getBefore()
  119. {
  120. return $this->beforeCalls;
  121. }
  122. public function after(call $call)
  123. {
  124. $this->setTrace();
  125. foreach (func_get_args() as $call)
  126. {
  127. $this->addAfterCall($call);
  128. }
  129. return $this;
  130. }
  131. public function getAfter()
  132. {
  133. return $this->afterCalls;
  134. }
  135. public function once($failMessage = null)
  136. {
  137. return $this->exactly(1, $failMessage);
  138. }
  139. public function twice($failMessage = null)
  140. {
  141. return $this->exactly(2, $failMessage);
  142. }
  143. public function thrice($failMessage = null)
  144. {
  145. return $this->exactly(3, $failMessage);
  146. }
  147. public function atLeastOnce($failMessage = null)
  148. {
  149. $this->removeFromManager();
  150. if ($this->countBeforeAndAfterCalls() >= 1)
  151. {
  152. $this->pass();
  153. }
  154. else
  155. {
  156. $this->fail($failMessage ?: $this->_('%s is called 0 time', $this->call) . $this->getCallsAsString());
  157. }
  158. return $this;
  159. }
  160. public function exactly($number, $failMessage = null)
  161. {
  162. $callsNumber = $this->removeFromManager()->countBeforeAndAfterCalls();
  163. if ((int) $number != $number)
  164. {
  165. throw new atoum\exceptions\logic\invalidArgument('Argument 1 of ' . __FUNCTION__ . ' must be an integer');
  166. }
  167. if ($callsNumber == $number)
  168. {
  169. $this->pass();
  170. }
  171. else
  172. {
  173. if ($failMessage === null)
  174. {
  175. $failMessage = $this->__('%s is called %d time instead of %d', '%s is called %d times instead of %d', $callsNumber, $this->call, $callsNumber, $number);
  176. if (sizeof($this->beforeCalls) > 0)
  177. {
  178. $beforeCalls = array();
  179. foreach ($this->beforeCalls as $asserter)
  180. {
  181. $beforeCalls[] = (string) $asserter->getCall();
  182. }
  183. $failMessage = $this->_('%s before %s', $failMessage, join(', ', $beforeCalls));
  184. }
  185. if (sizeof($this->afterCalls) > 0)
  186. {
  187. $afterCalls = array();
  188. foreach ($this->afterCalls as $asserter)
  189. {
  190. $afterCalls[] = (string) $asserter->getCall();
  191. }
  192. $failMessage = $this->_('%s after %s', $failMessage, join(', ', $afterCalls));
  193. }
  194. $failMessage .= $this->getCallsAsString();
  195. }
  196. $this->fail($failMessage);
  197. }
  198. return $this;
  199. }
  200. public function never($failMessage = null)
  201. {
  202. return $this->exactly(0, $failMessage);
  203. }
  204. public function getFunction()
  205. {
  206. return $this->call->getFunction();
  207. }
  208. public function getArguments()
  209. {
  210. return $this->adapterIsSet()->call->getArguments();
  211. }
  212. protected function adapterIsSet()
  213. {
  214. if ($this->adapter === null)
  215. {
  216. throw new exceptions\logic('Adapter is undefined');
  217. }
  218. return $this;
  219. }
  220. protected function callIsSet()
  221. {
  222. if ($this->adapterIsSet()->call->getFunction() === null)
  223. {
  224. throw new exceptions\logic('Call is undefined');
  225. }
  226. return $this;
  227. }
  228. protected function countBeforeAndAfterCalls()
  229. {
  230. $calls = $this->callIsSet()->adapter->getCalls($this->call, $this->identicalCall);
  231. if (sizeof($calls) > 0 && (sizeof($this->beforeCalls) > 0 || sizeof($this->afterCalls) > 0))
  232. {
  233. foreach ($this->beforeCalls as $asserter)
  234. {
  235. $pass = false;
  236. foreach ($calls->getTimeline() as $position => $call)
  237. {
  238. $hasAfterCalls = $asserter->hasAfterCalls($position);
  239. if ($hasAfterCalls === false)
  240. {
  241. $calls->removeCall($call, $position);
  242. }
  243. else if ($pass === false)
  244. {
  245. $pass = $hasAfterCalls;
  246. }
  247. }
  248. if ($pass === false)
  249. {
  250. $this->fail($this->_('%s is not called before %s', $this->call, $asserter->getCall()));
  251. }
  252. }
  253. foreach ($this->afterCalls as $asserter)
  254. {
  255. $pass = false;
  256. foreach ($calls->getTimeline() as $position => $call)
  257. {
  258. $hasPreviousCalls = $asserter->hasPreviousCalls($position);
  259. if ($hasPreviousCalls === false)
  260. {
  261. $calls->removeCall($call, $position);
  262. }
  263. else if ($pass === false)
  264. {
  265. $pass = $hasPreviousCalls;
  266. }
  267. }
  268. if ($pass === false)
  269. {
  270. $this->fail($this->_('%s is not called after %s', $this->call, $asserter->getCall()));
  271. }
  272. }
  273. }
  274. return sizeof($calls);
  275. }
  276. protected function setFunction($function)
  277. {
  278. $this
  279. ->adapterIsSet()
  280. ->setTrace()
  281. ->addToManager()
  282. ->call
  283. ->setFunction($function)
  284. ->unsetArguments()
  285. ;
  286. $this->beforeCalls = array();
  287. $this->afterCalls = array();
  288. return $this;
  289. }
  290. protected function setArguments(array $arguments)
  291. {
  292. $this
  293. ->adapterIsSet()
  294. ->callIsSet()
  295. ->setTrace()
  296. ->call
  297. ->setArguments($arguments)
  298. ;
  299. $this->identicalCall = false;
  300. return $this;
  301. }
  302. protected function unsetArguments()
  303. {
  304. $this
  305. ->adapterIsSet()
  306. ->callIsSet()
  307. ->setTrace()
  308. ->call
  309. ->unsetArguments()
  310. ;
  311. $this->identicalCall = false;
  312. return $this;
  313. }
  314. protected function setIdenticalArguments(array $arguments)
  315. {
  316. $this->setArguments($arguments)->identicalCall = true;
  317. return $this;
  318. }
  319. protected function hasPreviousCalls($position)
  320. {
  321. return $this->adapter->hasPreviousCalls($this->call, $position, $this->identicalCall);
  322. }
  323. protected function hasAfterCalls($position)
  324. {
  325. return $this->adapter->hasAfterCalls($this->call, $position, $this->identicalCall);
  326. }
  327. protected function getCalls($call)
  328. {
  329. return $this->adapter->getCalls($call);
  330. }
  331. protected function getCallsAsString()
  332. {
  333. $string = '';
  334. if (sizeof($this->beforeCalls) <= 0 && sizeof($this->afterCalls) <= 0)
  335. {
  336. $calls = $this->adapter->getCallsEqualTo($this->call->unsetArguments());
  337. $string = (sizeof($calls) <= 0 ? '' : PHP_EOL . rtrim($calls));
  338. }
  339. return $string;
  340. }
  341. protected function setTrace()
  342. {
  343. foreach (debug_backtrace() as $trace)
  344. {
  345. if (isset($trace['function']) === true && isset($trace['file']) === true && isset($trace['line']) === true)
  346. {
  347. if (isset($trace['object']) === false || $trace['object'] !== $this)
  348. {
  349. return $this;
  350. }
  351. $this->trace['file'] = $trace['file'];
  352. $this->trace['line'] = $trace['line'];
  353. }
  354. }
  355. $this->trace['file'] = null;
  356. $this->trace['line'] = null;
  357. return $this;
  358. }
  359. private function addBeforeCall(call $call)
  360. {
  361. $this->beforeCalls[] = $call->disableEvaluationChecking();
  362. return $this;
  363. }
  364. private function addAfterCall(call $call)
  365. {
  366. $this->afterCalls[] = $call->disableEvaluationChecking();
  367. return $this;
  368. }
  369. private function addToManager()
  370. {
  371. if ($this->manager !== null)
  372. {
  373. $this->manager->add($this);
  374. }
  375. return $this;
  376. }
  377. private function removeFromManager()
  378. {
  379. if ($this->manager !== null)
  380. {
  381. $this->manager->remove($this);
  382. }
  383. return $this;
  384. }
  385. }