TaskTest.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. <?php
  2. /*
  3. * Copyright 2014 Google Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. class TaskTest extends PHPUnit_Framework_TestCase
  18. {
  19. private $client;
  20. private $mockedCallsCount = 0;
  21. private $currentMockedCall = 0;
  22. private $mockedCalls = array();
  23. protected function setUp()
  24. {
  25. $this->client = new Google_Client();
  26. }
  27. /**
  28. * @dataProvider defaultRestErrorProvider
  29. * @expectedException Google_Service_Exception
  30. */
  31. public function testRestRetryOffByDefault($errorCode, $errorBody = '{}')
  32. {
  33. $this->setNextResponse($errorCode, $errorBody)->makeRequest();
  34. }
  35. /**
  36. * @dataProvider defaultRestErrorProvider
  37. * @expectedException Google_Service_Exception
  38. */
  39. public function testOneRestRetryWithError($errorCode, $errorBody = '{}')
  40. {
  41. $this->setTaskConfig(array('retries' => 1));
  42. $this->setNextResponses(2, $errorCode, $errorBody)->makeRequest();
  43. }
  44. /**
  45. * @dataProvider defaultRestErrorProvider
  46. * @expectedException Google_Service_Exception
  47. */
  48. public function testMultipleRestRetriesWithErrors(
  49. $errorCode,
  50. $errorBody = '{}'
  51. ) {
  52. $this->setTaskConfig(array('retries' => 5));
  53. $this->setNextResponses(6, $errorCode, $errorBody)->makeRequest();
  54. }
  55. /**
  56. * @dataProvider defaultRestErrorProvider
  57. */
  58. public function testOneRestRetryWithSuccess($errorCode, $errorBody = '{}')
  59. {
  60. $this->setTaskConfig(array('retries' => 1));
  61. $result = $this->setNextResponse($errorCode, $errorBody)
  62. ->setNextResponse(200, '{"success": true}')
  63. ->makeRequest();
  64. $this->assertEquals(array("success" => true), $result);
  65. }
  66. /**
  67. * @dataProvider defaultRestErrorProvider
  68. */
  69. public function testMultipleRestRetriesWithSuccess(
  70. $errorCode,
  71. $errorBody = '{}'
  72. ) {
  73. $this->setTaskConfig(array('retries' => 5));
  74. $result = $this->setNextResponses(2, $errorCode, $errorBody)
  75. ->setNextResponse(200, '{"success": true}')
  76. ->makeRequest();
  77. $this->assertEquals(array("success" => true), $result);
  78. }
  79. /**
  80. * @dataProvider defaultRestErrorProvider
  81. * @expectedException Google_Service_Exception
  82. */
  83. public function testCustomRestRetryMapReplacesDefaults(
  84. $errorCode,
  85. $errorBody = '{}'
  86. ) {
  87. $this->client->setClassConfig(
  88. 'Google_Service_Exception',
  89. array('retry_map' => array())
  90. );
  91. $this->setTaskConfig(array('retries' => 5));
  92. $this->setNextResponse($errorCode, $errorBody)->makeRequest();
  93. }
  94. public function testCustomRestRetryMapAddsNewHandlers()
  95. {
  96. $this->client->setClassConfig(
  97. 'Google_Service_Exception',
  98. array('retry_map' => array('403' => Google_Config::TASK_RETRY_ALWAYS))
  99. );
  100. $this->setTaskConfig(array('retries' => 5));
  101. $result = $this->setNextResponses(2, 403)
  102. ->setNextResponse(200, '{"success": true}')
  103. ->makeRequest();
  104. $this->assertEquals(array("success" => true), $result);
  105. }
  106. /**
  107. * @expectedException Google_Service_Exception
  108. * @dataProvider customLimitsProvider
  109. */
  110. public function testCustomRestRetryMapWithCustomLimits($limit)
  111. {
  112. $this->client->setClassConfig(
  113. 'Google_Service_Exception',
  114. array('retry_map' => array('403' => $limit))
  115. );
  116. $this->setTaskConfig(array('retries' => 5));
  117. $this->setNextResponses($limit + 1, 403)->makeRequest();
  118. }
  119. /**
  120. * @dataProvider timeoutProvider
  121. */
  122. public function testRestTimeouts($config, $minTime)
  123. {
  124. $this->setTaskConfig($config);
  125. $this->setNextResponses($config['retries'], 500)
  126. ->setNextResponse(200, '{"success": true}');
  127. $this->assertTaskTimeGreaterThanOrEqual(
  128. $minTime,
  129. array($this, 'makeRequest'),
  130. $config['initial_delay'] / 10
  131. );
  132. }
  133. /**
  134. * @requires extension curl
  135. * @dataProvider defaultCurlErrorProvider
  136. * @expectedException Google_IO_Exception
  137. */
  138. public function testCurlRetryOffByDefault($errorCode, $errorMessage = '')
  139. {
  140. $this->setNextResponseThrows($errorMessage, $errorCode)->makeRequest();
  141. }
  142. /**
  143. * @requires extension curl
  144. * @dataProvider defaultCurlErrorProvider
  145. * @expectedException Google_IO_Exception
  146. */
  147. public function testOneCurlRetryWithError($errorCode, $errorMessage = '')
  148. {
  149. $this->setTaskConfig(array('retries' => 1));
  150. $this->setNextResponsesThrow(2, $errorMessage, $errorCode)->makeRequest();
  151. }
  152. /**
  153. * @requires extension curl
  154. * @dataProvider defaultCurlErrorProvider
  155. * @expectedException Google_IO_Exception
  156. */
  157. public function testMultipleCurlRetriesWithErrors(
  158. $errorCode,
  159. $errorMessage = ''
  160. ) {
  161. $this->setTaskConfig(array('retries' => 5));
  162. $this->setNextResponsesThrow(6, $errorMessage, $errorCode)->makeRequest();
  163. }
  164. /**
  165. * @requires extension curl
  166. * @dataProvider defaultCurlErrorProvider
  167. */
  168. public function testOneCurlRetryWithSuccess($errorCode, $errorMessage = '')
  169. {
  170. $this->setTaskConfig(array('retries' => 1));
  171. $result = $this->setNextResponseThrows($errorMessage, $errorCode)
  172. ->setNextResponse(200, '{"success": true}')
  173. ->makeRequest();
  174. $this->assertEquals(array("success" => true), $result);
  175. }
  176. /**
  177. * @requires extension curl
  178. * @dataProvider defaultCurlErrorProvider
  179. */
  180. public function testMultipleCurlRetriesWithSuccess(
  181. $errorCode,
  182. $errorMessage = ''
  183. ) {
  184. $this->setTaskConfig(array('retries' => 5));
  185. $result = $this->setNextResponsesThrow(2, $errorMessage, $errorCode)
  186. ->setNextResponse(200, '{"success": true}')
  187. ->makeRequest();
  188. $this->assertEquals(array("success" => true), $result);
  189. }
  190. /**
  191. * @requires extension curl
  192. * @dataProvider defaultCurlErrorProvider
  193. * @expectedException Google_IO_Exception
  194. */
  195. public function testCustomCurlRetryMapReplacesDefaults(
  196. $errorCode,
  197. $errorMessage = ''
  198. ) {
  199. $this->client->setClassConfig(
  200. 'Google_IO_Exception',
  201. array('retry_map' => array())
  202. );
  203. $this->setTaskConfig(array('retries' => 5));
  204. $this->setNextResponseThrows($errorMessage, $errorCode)->makeRequest();
  205. }
  206. /**
  207. * @requires extension curl
  208. */
  209. public function testCustomCurlRetryMapAddsNewHandlers()
  210. {
  211. $this->client->setClassConfig(
  212. 'Google_IO_Exception',
  213. array('retry_map' => array(
  214. CURLE_COULDNT_RESOLVE_PROXY => Google_Config::TASK_RETRY_ALWAYS
  215. ))
  216. );
  217. $this->setTaskConfig(array('retries' => 5));
  218. $result = $this->setNextResponsesThrow(2, '', CURLE_COULDNT_RESOLVE_PROXY)
  219. ->setNextResponse(200, '{"success": true}')
  220. ->makeRequest();
  221. $this->assertEquals(array("success" => true), $result);
  222. }
  223. /**
  224. * @requires extension curl
  225. * @expectedException Google_IO_Exception
  226. * @dataProvider customLimitsProvider
  227. */
  228. public function testCustomCurlRetryMapWithCustomLimits($limit)
  229. {
  230. $this->client->setClassConfig(
  231. 'Google_IO_Exception',
  232. array('retry_map' => array(
  233. CURLE_COULDNT_RESOLVE_PROXY => $limit
  234. ))
  235. );
  236. $this->setTaskConfig(array('retries' => 5));
  237. $this->setNextResponsesThrow($limit + 1, '', CURLE_COULDNT_RESOLVE_PROXY)
  238. ->makeRequest();
  239. }
  240. /**
  241. * @requires extension curl
  242. * @dataProvider timeoutProvider
  243. */
  244. public function testCurlTimeouts($config, $minTime)
  245. {
  246. $this->setTaskConfig($config);
  247. $this->setNextResponsesThrow($config['retries'], '', CURLE_GOT_NOTHING)
  248. ->setNextResponse(200, '{"success": true}');
  249. $this->assertTaskTimeGreaterThanOrEqual(
  250. $minTime,
  251. array($this, 'makeRequest'),
  252. $config['initial_delay'] / 10
  253. );
  254. }
  255. /**
  256. * @dataProvider badTaskConfigProvider
  257. */
  258. public function testBadTaskConfig($config, $message)
  259. {
  260. $this->setExpectedException('Google_Task_Exception', $message);
  261. $this->setTaskConfig($config);
  262. new Google_Task_Runner(
  263. $this->client,
  264. '',
  265. array($this, 'testBadTaskConfig')
  266. );
  267. }
  268. /**
  269. * @expectedException Google_Task_Exception
  270. * @expectedExceptionMessage must be a valid callable
  271. */
  272. public function testBadTaskCallback()
  273. {
  274. new Google_Task_Runner($this->client, '', 5);
  275. }
  276. /**
  277. * @expectedException Google_IO_Exception
  278. */
  279. public function testTaskRetryOffByDefault()
  280. {
  281. $this->setNextTaskAllowedRetries(Google_Config::TASK_RETRY_ALWAYS)
  282. ->runTask();
  283. }
  284. /**
  285. * @expectedException Google_IO_Exception
  286. */
  287. public function testOneTaskRetryWithError()
  288. {
  289. $this->setTaskConfig(array('retries' => 1));
  290. $this->setNextTasksAllowedRetries(2, Google_Config::TASK_RETRY_ALWAYS)
  291. ->runTask();
  292. }
  293. /**
  294. * @expectedException Google_IO_Exception
  295. */
  296. public function testMultipleTaskRetriesWithErrors()
  297. {
  298. $this->setTaskConfig(array('retries' => 5));
  299. $this->setNextTasksAllowedRetries(6, Google_Config::TASK_RETRY_ALWAYS)
  300. ->runTask();
  301. }
  302. public function testOneTaskRetryWithSuccess()
  303. {
  304. $this->setTaskConfig(array('retries' => 1));
  305. $result = $this->setNextTaskAllowedRetries(Google_Config::TASK_RETRY_ALWAYS)
  306. ->setNextTaskReturnValue('success')
  307. ->runTask();
  308. $this->assertEquals('success', $result);
  309. }
  310. public function testMultipleTaskRetriesWithSuccess()
  311. {
  312. $this->setTaskConfig(array('retries' => 5));
  313. $result = $this->setNextTasksAllowedRetries(2, Google_Config::TASK_RETRY_ALWAYS)
  314. ->setNextTaskReturnValue('success')
  315. ->runTask();
  316. $this->assertEquals('success', $result);
  317. }
  318. /**
  319. * @expectedException Google_IO_Exception
  320. * @dataProvider customLimitsProvider
  321. */
  322. public function testTaskRetryWithCustomLimits($limit)
  323. {
  324. $this->setTaskConfig(array('retries' => 5));
  325. $this->setNextTasksAllowedRetries($limit + 1, $limit)
  326. ->runTask();
  327. }
  328. /**
  329. * @dataProvider timeoutProvider
  330. */
  331. public function testTaskTimeouts($config, $minTime)
  332. {
  333. $this->setTaskConfig($config);
  334. $this->setNextTasksAllowedRetries($config['retries'], $config['retries'] + 1)
  335. ->setNextTaskReturnValue('success');
  336. $this->assertTaskTimeGreaterThanOrEqual(
  337. $minTime,
  338. array($this, 'runTask'),
  339. $config['initial_delay'] / 10
  340. );
  341. }
  342. public function testTaskWithManualRetries()
  343. {
  344. $this->setTaskConfig(array('retries' => 2));
  345. $this->setNextTasksAllowedRetries(2, Google_Config::TASK_RETRY_ALWAYS);
  346. $task = new Google_Task_Runner(
  347. $this->client,
  348. '',
  349. array($this, 'runNextTask')
  350. );
  351. $this->assertTrue($task->canAttmpt());
  352. $this->assertTrue($task->attempt());
  353. $this->assertTrue($task->canAttmpt());
  354. $this->assertTrue($task->attempt());
  355. $this->assertTrue($task->canAttmpt());
  356. $this->assertTrue($task->attempt());
  357. $this->assertFalse($task->canAttmpt());
  358. $this->assertFalse($task->attempt());
  359. }
  360. /**
  361. * Provider for backoff configurations and expected minimum runtimes.
  362. *
  363. * @return array
  364. */
  365. public function timeoutProvider()
  366. {
  367. $config = array('initial_delay' => .001, 'max_delay' => .01);
  368. return array(
  369. array(array_merge($config, array('retries' => 1)), .001),
  370. array(array_merge($config, array('retries' => 2)), .0015),
  371. array(array_merge($config, array('retries' => 3)), .00225),
  372. array(array_merge($config, array('retries' => 4)), .00375),
  373. array(array_merge($config, array('retries' => 5)), .005625)
  374. );
  375. }
  376. /**
  377. * Provider for custom retry limits.
  378. *
  379. * @return array
  380. */
  381. public function customLimitsProvider()
  382. {
  383. return array(
  384. array(Google_Config::TASK_RETRY_NEVER),
  385. array(Google_Config::TASK_RETRY_ONCE),
  386. );
  387. }
  388. /**
  389. * Provider for invalid task configurations.
  390. *
  391. * @return array
  392. */
  393. public function badTaskConfigProvider()
  394. {
  395. return array(
  396. array(array('initial_delay' => -1), 'must not be negative'),
  397. array(array('max_delay' => 0), 'must be greater than 0'),
  398. array(array('factor' => 0), 'must be greater than 0'),
  399. array(array('jitter' => 0), 'must be greater than 0'),
  400. array(array('retries' => -1), 'must not be negative')
  401. );
  402. }
  403. /**
  404. * Provider for the default REST errors.
  405. *
  406. * @return array
  407. */
  408. public function defaultRestErrorProvider()
  409. {
  410. return array(
  411. array(500),
  412. array(503),
  413. array(403, '{"error":{"errors":[{"reason":"rateLimitExceeded"}]}}'),
  414. array(403, '{"error":{"errors":[{"reason":"userRateLimitExceeded"}]}}'),
  415. );
  416. }
  417. /**
  418. * Provider for the default cURL errors.
  419. *
  420. * @return array
  421. */
  422. public function defaultCurlErrorProvider()
  423. {
  424. return array(
  425. array(6), // CURLE_COULDNT_RESOLVE_HOST
  426. array(7), // CURLE_COULDNT_CONNECT
  427. array(28), // CURLE_OPERATION_TIMEOUTED
  428. array(35), // CURLE_SSL_CONNECT_ERROR
  429. array(52), // CURLE_GOT_NOTHING
  430. );
  431. }
  432. /**
  433. * Assert the minimum amount of time required to run a task.
  434. *
  435. * NOTE: Intentionally crude for brevity.
  436. *
  437. * @param float $expected The expected minimum execution time
  438. * @param callable $callback The task to time
  439. * @param float $delta Allowable relative error
  440. *
  441. * @throws PHPUnit_Framework_ExpectationFailedException
  442. */
  443. public static function assertTaskTimeGreaterThanOrEqual(
  444. $expected,
  445. $callback,
  446. $delta = 0.0
  447. ) {
  448. $time = microtime(true);
  449. call_user_func($callback);
  450. self::assertThat(
  451. microtime(true) - $time,
  452. self::logicalOr(
  453. self::greaterThan($expected),
  454. self::equalTo($expected, $delta)
  455. )
  456. );
  457. }
  458. /**
  459. * Sets the task runner configurations.
  460. *
  461. * @param array $config The task runner configurations
  462. */
  463. private function setTaskConfig(array $config)
  464. {
  465. $config += array(
  466. 'initial_delay' => .0001,
  467. 'max_delay' => .001,
  468. 'factor' => 2,
  469. 'jitter' => .5,
  470. 'retries' => 1
  471. );
  472. $this->client->setClassConfig('Google_Task_Runner', $config);
  473. }
  474. /**
  475. * Sets the next responses.
  476. *
  477. * @param integer $count The number of responses
  478. * @param string $code The response code
  479. * @param string $body The response body
  480. * @param array $headers The response headers
  481. *
  482. * @return TaskTest
  483. */
  484. private function setNextResponses(
  485. $count,
  486. $code = '200',
  487. $body = '{}',
  488. array $headers = array()
  489. ) {
  490. while ($count-- > 0) {
  491. $this->setNextResponse($code, $body, $headers);
  492. }
  493. return $this;
  494. }
  495. /**
  496. * Sets the next response.
  497. *
  498. * @param string $code The response code
  499. * @param string $body The response body
  500. * @param array $headers The response headers
  501. *
  502. * @return TaskTest
  503. */
  504. private function setNextResponse(
  505. $code = '200',
  506. $body = '{}',
  507. array $headers = array()
  508. ) {
  509. $this->mockedCalls[$this->mockedCallsCount++] = array(
  510. 'code' => (string) $code,
  511. 'headers' => $headers,
  512. 'body' => is_string($body) ? $body : json_encode($body)
  513. );
  514. return $this;
  515. }
  516. /**
  517. * Forces the next responses to throw an IO exception.
  518. *
  519. * @param integer $count The number of responses
  520. * @param string $message The exception messages
  521. * @param string $code The exception code
  522. *
  523. * @return TaskTest
  524. */
  525. private function setNextResponsesThrow($count, $message, $code)
  526. {
  527. while ($count-- > 0) {
  528. $this->setNextResponseThrows($message, $code);
  529. }
  530. return $this;
  531. }
  532. /**
  533. * Forces the next response to throw an IO exception.
  534. *
  535. * @param string $message The exception messages
  536. * @param string $code The exception code
  537. *
  538. * @return TaskTest
  539. */
  540. private function setNextResponseThrows($message, $code)
  541. {
  542. $map = $this->client->getClassConfig('Google_IO_Exception', 'retry_map');
  543. $this->mockedCalls[$this->mockedCallsCount++] = new Google_IO_Exception(
  544. $message,
  545. $code,
  546. null,
  547. $map
  548. );
  549. return $this;
  550. }
  551. /**
  552. * Runs the defined request.
  553. *
  554. * @return array
  555. */
  556. private function makeRequest()
  557. {
  558. $request = new Google_Http_Request('/test');
  559. $io = $this->getMockBuilder('Google_IO_Abstract')
  560. ->disableOriginalConstructor()
  561. ->setMethods(array('makeRequest'))
  562. ->getMockForAbstractClass();
  563. $io->expects($this->exactly($this->mockedCallsCount))
  564. ->method('makeRequest')
  565. ->will($this->returnCallback(array($this, 'getNextMockedCall')));
  566. $this->client->setIo($io);
  567. return Google_Http_REST::execute($this->client, $request);
  568. }
  569. /**
  570. * Gets the next mocked response.
  571. *
  572. * @param Google_Http_Request $request The mocked request
  573. *
  574. * @return Google_Http_Request
  575. */
  576. public function getNextMockedCall($request)
  577. {
  578. $current = $this->mockedCalls[$this->currentMockedCall++];
  579. if ($current instanceof Exception) {
  580. throw $current;
  581. }
  582. $request->setResponseHttpCode($current['code']);
  583. $request->setResponseHeaders($current['headers']);
  584. $request->setResponseBody($current['body']);
  585. return $request;
  586. }
  587. /**
  588. * Sets the next task return value.
  589. *
  590. * @param mixed $value The next return value
  591. *
  592. * @return TaskTest
  593. */
  594. private function setNextTaskReturnValue($value)
  595. {
  596. $this->mockedCalls[$this->mockedCallsCount++] = $value;
  597. return $this;
  598. }
  599. /**
  600. * Sets the next exception `allowedRetries()` return value.
  601. *
  602. * @param boolean $allowedRetries The next `allowedRetries()` return value.
  603. *
  604. * @return TaskTest
  605. */
  606. private function setNextTaskAllowedRetries($allowedRetries)
  607. {
  608. $this->mockedCalls[$this->mockedCallsCount++] = $allowedRetries;
  609. return $this;
  610. }
  611. /**
  612. * Sets multiple exception `allowedRetries()` return value.
  613. *
  614. * @param integer $count The number of `allowedRetries()` return values.
  615. * @param boolean $allowedRetries The `allowedRetries()` return value.
  616. *
  617. * @return TaskTest
  618. */
  619. private function setNextTasksAllowedRetries($count, $allowedRetries)
  620. {
  621. while ($count-- > 0) {
  622. $this->setNextTaskAllowedRetries($allowedRetries);
  623. }
  624. return $this;
  625. }
  626. /**
  627. * Runs the defined task.
  628. *
  629. * @return mixed
  630. */
  631. private function runTask()
  632. {
  633. $task = new Google_Task_Runner(
  634. $this->client,
  635. '',
  636. array($this, 'runNextTask')
  637. );
  638. $exception = $this->getMockBuilder('Google_IO_Exception')
  639. ->disableOriginalConstructor()
  640. ->setMethods(array('allowedRetries'))
  641. ->getMock();
  642. $exceptionCount = 0;
  643. $exceptionCalls = array();
  644. for ($i = 0; $i < $this->mockedCallsCount; $i++) {
  645. if (is_int($this->mockedCalls[$i])) {
  646. $exceptionCalls[$exceptionCount++] = $this->mockedCalls[$i];
  647. $this->mockedCalls[$i] = $exception;
  648. }
  649. }
  650. $exception->expects($this->exactly($exceptionCount))
  651. ->method('allowedRetries')
  652. ->will(
  653. new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls(
  654. $exceptionCalls
  655. )
  656. );
  657. return $task->run();
  658. }
  659. /**
  660. * Gets the next task return value.
  661. *
  662. * @return mixed
  663. */
  664. public function runNextTask()
  665. {
  666. $current = $this->mockedCalls[$this->currentMockedCall++];
  667. if ($current instanceof Exception) {
  668. throw $current;
  669. }
  670. return $current;
  671. }
  672. }