UtilsTest.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. <?php
  2. declare(strict_types=1);
  3. namespace GuzzleHttp\Promise\Tests;
  4. use GuzzleHttp\Promise\AggregateException;
  5. use GuzzleHttp\Promise as P;
  6. use GuzzleHttp\Promise\FulfilledPromise;
  7. use GuzzleHttp\Promise\Promise;
  8. use GuzzleHttp\Promise\PromiseInterface;
  9. use GuzzleHttp\Promise\RejectedPromise;
  10. use GuzzleHttp\Promise\RejectionException;
  11. use GuzzleHttp\Promise\TaskQueue;
  12. use PHPUnit\Framework\TestCase;
  13. class UtilsTest extends TestCase
  14. {
  15. public function testWaitsOnAllPromisesIntoArray(): void
  16. {
  17. $e = new \Exception();
  18. $a = new Promise(function () use (&$a): void { $a->resolve('a'); });
  19. $b = new Promise(function () use (&$b): void { $b->reject('b'); });
  20. $c = new Promise(function () use (&$c, $e): void { $c->reject($e); });
  21. $results = P\Utils::inspectAll([$a, $b, $c]);
  22. $this->assertSame([
  23. ['state' => 'fulfilled', 'value' => 'a'],
  24. ['state' => 'rejected', 'reason' => 'b'],
  25. ['state' => 'rejected', 'reason' => $e],
  26. ], $results);
  27. }
  28. public function testUnwrapsPromisesWithNoDefaultAndFailure(): void
  29. {
  30. $this->expectException(\GuzzleHttp\Promise\RejectionException::class);
  31. $promises = [new FulfilledPromise('a'), new Promise()];
  32. P\Utils::unwrap($promises);
  33. }
  34. public function testUnwrapsPromisesWithNoDefault(): void
  35. {
  36. $promises = [new FulfilledPromise('a')];
  37. $this->assertSame(['a'], P\Utils::unwrap($promises));
  38. }
  39. public function testUnwrapsPromisesWithKeys(): void
  40. {
  41. $promises = [
  42. 'foo' => new FulfilledPromise('a'),
  43. 'bar' => new FulfilledPromise('b'),
  44. ];
  45. $this->assertSame([
  46. 'foo' => 'a',
  47. 'bar' => 'b',
  48. ], P\Utils::unwrap($promises));
  49. }
  50. public function testAllAggregatesSortedArray(): void
  51. {
  52. $a = new Promise();
  53. $b = new Promise();
  54. $c = new Promise();
  55. $d = P\Utils::all([$a, $b, $c]);
  56. $b->resolve('b');
  57. $a->resolve('a');
  58. $c->resolve('c');
  59. $d->then(
  60. function ($value) use (&$result): void { $result = $value; },
  61. function ($reason) use (&$result): void { $result = $reason; }
  62. );
  63. P\Utils::queue()->run();
  64. $this->assertSame(['a', 'b', 'c'], $result);
  65. }
  66. public function testPromisesDynamicallyAddedToStack(): void
  67. {
  68. $promises = new \ArrayIterator();
  69. $counter = 0;
  70. $promises['a'] = new FulfilledPromise('a');
  71. $promises['b'] = $promise = new Promise(function () use (&$promise, &$promises, &$counter): void {
  72. ++$counter; // Make sure the wait function is called only once
  73. $promise->resolve('b');
  74. $promises['c'] = $subPromise = new Promise(function () use (&$subPromise): void {
  75. $subPromise->resolve('c');
  76. });
  77. });
  78. $result = P\Utils::all($promises, true)->wait();
  79. $this->assertCount(3, $promises);
  80. $this->assertCount(3, $result);
  81. $this->assertSame($result['c'], 'c');
  82. $this->assertSame(1, $counter);
  83. }
  84. public function testAllThrowsWhenAnyRejected(): void
  85. {
  86. $a = new Promise();
  87. $b = new Promise();
  88. $c = new Promise();
  89. $d = P\Utils::all([$a, $b, $c]);
  90. $b->resolve('b');
  91. $a->reject('fail');
  92. $c->resolve('c');
  93. $d->then(
  94. function ($value) use (&$result): void { $result = $value; },
  95. function ($reason) use (&$result): void { $result = $reason; }
  96. );
  97. P\Utils::queue()->run();
  98. $this->assertSame('fail', $result);
  99. }
  100. public function testSomeAggregatesSortedArrayWithMax(): void
  101. {
  102. $a = new Promise();
  103. $b = new Promise();
  104. $c = new Promise();
  105. $d = P\Utils::some(2, [$a, $b, $c]);
  106. $b->resolve('b');
  107. $c->resolve('c');
  108. $a->resolve('a');
  109. $d->then(function ($value) use (&$result): void { $result = $value; });
  110. P\Utils::queue()->run();
  111. $this->assertSame(['b', 'c'], $result);
  112. }
  113. public function testSomeRejectsWhenTooManyRejections(): void
  114. {
  115. $a = new Promise();
  116. $b = new Promise();
  117. $d = P\Utils::some(2, [$a, $b]);
  118. $a->reject('bad');
  119. $b->resolve('good');
  120. P\Utils::queue()->run();
  121. $this->assertTrue(P\Is::rejected($d));
  122. $d->then(null, function ($reason) use (&$called): void {
  123. $called = $reason;
  124. });
  125. P\Utils::queue()->run();
  126. $this->assertInstanceOf(AggregateException::class, $called);
  127. $this->assertContains('bad', $called->getReason());
  128. }
  129. public function testCanWaitUntilSomeCountIsSatisfied(): void
  130. {
  131. $a = new Promise(function () use (&$a): void { $a->resolve('a'); });
  132. $b = new Promise(function () use (&$b): void { $b->resolve('b'); });
  133. $c = new Promise(function () use (&$c): void { $c->resolve('c'); });
  134. $d = P\Utils::some(2, [$a, $b, $c]);
  135. $this->assertSame(['a', 'b'], $d->wait());
  136. }
  137. public function testThrowsIfImpossibleToWaitForSomeCount(): void
  138. {
  139. $this->expectException(\GuzzleHttp\Promise\AggregateException::class);
  140. $this->expectExceptionMessage('Not enough promises to fulfill count');
  141. $a = new Promise(function () use (&$a): void { $a->resolve('a'); });
  142. $d = P\Utils::some(2, [$a]);
  143. $d->wait();
  144. }
  145. public function testThrowsIfResolvedWithoutCountTotalResults(): void
  146. {
  147. $this->expectException(\GuzzleHttp\Promise\AggregateException::class);
  148. $this->expectExceptionMessage('Not enough promises to fulfill count');
  149. $a = new Promise();
  150. $b = new Promise();
  151. $d = P\Utils::some(3, [$a, $b]);
  152. $a->resolve('a');
  153. $b->resolve('b');
  154. $d->wait();
  155. }
  156. public function testAnyReturnsFirstMatch(): void
  157. {
  158. $a = new Promise();
  159. $b = new Promise();
  160. $c = P\Utils::any([$a, $b]);
  161. $b->resolve('b');
  162. $a->resolve('a');
  163. $c->then(function ($value) use (&$result): void { $result = $value; });
  164. P\Utils::queue()->run();
  165. $this->assertSame('b', $result);
  166. }
  167. public function testSettleFulfillsWithFulfilledAndRejected(): void
  168. {
  169. $a = new Promise();
  170. $b = new Promise();
  171. $c = new Promise();
  172. $d = P\Utils::settle([$a, $b, $c]);
  173. $b->resolve('b');
  174. $c->resolve('c');
  175. $a->reject('a');
  176. P\Utils::queue()->run();
  177. $this->assertTrue(P\Is::fulfilled($d));
  178. $d->then(function ($value) use (&$result): void { $result = $value; });
  179. P\Utils::queue()->run();
  180. $this->assertSame([
  181. ['state' => 'rejected', 'reason' => 'a'],
  182. ['state' => 'fulfilled', 'value' => 'b'],
  183. ['state' => 'fulfilled', 'value' => 'c'],
  184. ], $result);
  185. }
  186. public function testCanInspectFulfilledPromise(): void
  187. {
  188. $p = new FulfilledPromise('foo');
  189. $this->assertSame([
  190. 'state' => 'fulfilled',
  191. 'value' => 'foo',
  192. ], P\Utils::inspect($p));
  193. }
  194. public function testCanInspectRejectedPromise(): void
  195. {
  196. $p = new RejectedPromise('foo');
  197. $this->assertSame([
  198. 'state' => 'rejected',
  199. 'reason' => 'foo',
  200. ], P\Utils::inspect($p));
  201. }
  202. public function testCanInspectRejectedPromiseWithNormalException(): void
  203. {
  204. $e = new \Exception('foo');
  205. $p = new RejectedPromise($e);
  206. $this->assertSame([
  207. 'state' => 'rejected',
  208. 'reason' => $e,
  209. ], P\Utils::inspect($p));
  210. }
  211. public function testReturnsTrampoline(): void
  212. {
  213. $this->assertInstanceOf(TaskQueue::class, P\Utils::queue());
  214. $this->assertSame(P\Utils::queue(), P\Utils::queue());
  215. }
  216. public function testCanScheduleThunk(): void
  217. {
  218. $tramp = P\Utils::queue();
  219. $promise = P\Utils::task(function () { return 'Hi!'; });
  220. $c = null;
  221. $promise->then(function ($v) use (&$c): void { $c = $v; });
  222. $this->assertNull($c);
  223. $tramp->run();
  224. $this->assertSame('Hi!', $c);
  225. }
  226. public function testCanScheduleThunkWithRejection(): void
  227. {
  228. $tramp = P\Utils::queue();
  229. $promise = P\Utils::task(function (): void { throw new \Exception('Hi!'); });
  230. $c = null;
  231. $promise->otherwise(function ($v) use (&$c): void { $c = $v; });
  232. $this->assertNull($c);
  233. $tramp->run();
  234. $this->assertSame('Hi!', $c->getMessage());
  235. }
  236. public function testCanScheduleThunkWithWait(): void
  237. {
  238. $tramp = P\Utils::queue();
  239. $promise = P\Utils::task(function () { return 'a'; });
  240. $this->assertSame('a', $promise->wait());
  241. $tramp->run();
  242. }
  243. public function testYieldsFromCoroutine(): void
  244. {
  245. if (defined('HHVM_VERSION')) {
  246. $this->markTestIncomplete('Broken on HHVM.');
  247. }
  248. $promise = P\Coroutine::of(function () {
  249. $value = (yield new FulfilledPromise('a'));
  250. yield $value.'b';
  251. });
  252. $promise->then(function ($value) use (&$result): void { $result = $value; });
  253. P\Utils::queue()->run();
  254. $this->assertSame('ab', $result);
  255. }
  256. public function testCanCatchExceptionsInCoroutine(): void
  257. {
  258. if (defined('HHVM_VERSION')) {
  259. $this->markTestIncomplete('Broken on HHVM.');
  260. }
  261. $promise = P\Coroutine::of(function () {
  262. try {
  263. yield new RejectedPromise('a');
  264. $this->fail('Should have thrown into the coroutine!');
  265. } catch (RejectionException $e) {
  266. $value = (yield new FulfilledPromise($e->getReason()));
  267. yield $value.'b';
  268. }
  269. });
  270. $promise->then(function ($value) use (&$result): void { $result = $value; });
  271. P\Utils::queue()->run();
  272. $this->assertTrue(P\Is::fulfilled($promise));
  273. $this->assertSame('ab', $result);
  274. }
  275. /**
  276. * @dataProvider rejectsParentExceptionProvider
  277. */
  278. public function testRejectsParentExceptionWhenException(PromiseInterface $promise): void
  279. {
  280. $promise->then(
  281. function (): void { $this->fail(); },
  282. function ($reason) use (&$result): void { $result = $reason; }
  283. );
  284. P\Utils::queue()->run();
  285. $this->assertInstanceOf(\Exception::class, $result);
  286. $this->assertSame('a', $result->getMessage());
  287. }
  288. public function rejectsParentExceptionProvider()
  289. {
  290. return [
  291. [P\Coroutine::of(function () {
  292. yield new FulfilledPromise(0);
  293. throw new \Exception('a');
  294. })],
  295. [P\Coroutine::of(function () {
  296. throw new \Exception('a');
  297. yield new FulfilledPromise(0);
  298. })],
  299. ];
  300. }
  301. public function testCanRejectFromRejectionCallback(): void
  302. {
  303. if (defined('HHVM_VERSION')) {
  304. $this->markTestIncomplete('Broken on HHVM.');
  305. }
  306. $promise = P\Coroutine::of(function () {
  307. yield new FulfilledPromise(0);
  308. yield new RejectedPromise('no!');
  309. });
  310. $promise->then(
  311. function (): void { $this->fail(); },
  312. function ($reason) use (&$result): void { $result = $reason; }
  313. );
  314. P\Utils::queue()->run();
  315. $this->assertInstanceOf(RejectionException::class, $result);
  316. $this->assertSame('no!', $result->getReason());
  317. }
  318. public function testCanAsyncReject(): void
  319. {
  320. if (defined('HHVM_VERSION')) {
  321. $this->markTestIncomplete('Broken on HHVM.');
  322. }
  323. $rej = new Promise();
  324. $promise = P\Coroutine::of(function () use ($rej) {
  325. yield new FulfilledPromise(0);
  326. yield $rej;
  327. });
  328. $promise->then(
  329. function (): void { $this->fail(); },
  330. function ($reason) use (&$result): void { $result = $reason; }
  331. );
  332. $rej->reject('no!');
  333. P\Utils::queue()->run();
  334. $this->assertInstanceOf(RejectionException::class, $result);
  335. $this->assertSame('no!', $result->getReason());
  336. }
  337. public function testCanCatchAndThrowOtherException(): void
  338. {
  339. $promise = P\Coroutine::of(function () {
  340. try {
  341. yield new RejectedPromise('a');
  342. $this->fail('Should have thrown into the coroutine!');
  343. } catch (RejectionException $e) {
  344. throw new \Exception('foo');
  345. }
  346. });
  347. $promise->otherwise(function ($value) use (&$result): void { $result = $value; });
  348. P\Utils::queue()->run();
  349. $this->assertTrue(P\Is::rejected($promise));
  350. $this->assertStringContainsString('foo', $result->getMessage());
  351. }
  352. public function testCanCatchAndYieldOtherException(): void
  353. {
  354. if (defined('HHVM_VERSION')) {
  355. $this->markTestIncomplete('Broken on HHVM.');
  356. }
  357. $promise = P\Coroutine::of(function () {
  358. try {
  359. yield new RejectedPromise('a');
  360. $this->fail('Should have thrown into the coroutine!');
  361. } catch (RejectionException $e) {
  362. yield new RejectedPromise('foo');
  363. }
  364. });
  365. $promise->otherwise(function ($value) use (&$result): void { $result = $value; });
  366. P\Utils::queue()->run();
  367. $this->assertTrue(P\Is::rejected($promise));
  368. $this->assertStringContainsString('foo', $result->getMessage());
  369. }
  370. public function createLotsOfSynchronousPromise()
  371. {
  372. return P\Coroutine::of(function () {
  373. $value = 0;
  374. for ($i = 0; $i < 1000; ++$i) {
  375. $value = (yield new FulfilledPromise($i));
  376. }
  377. yield $value;
  378. });
  379. }
  380. public function testLotsOfSynchronousDoesNotBlowStack(): void
  381. {
  382. if (defined('HHVM_VERSION')) {
  383. $this->markTestIncomplete('Broken on HHVM.');
  384. }
  385. $promise = $this->createLotsOfSynchronousPromise();
  386. $promise->then(function ($v) use (&$r): void { $r = $v; });
  387. P\Utils::queue()->run();
  388. $this->assertSame(999, $r);
  389. }
  390. public function testLotsOfSynchronousWaitDoesNotBlowStack(): void
  391. {
  392. if (defined('HHVM_VERSION')) {
  393. $this->markTestIncomplete('Broken on HHVM.');
  394. }
  395. $promise = $this->createLotsOfSynchronousPromise();
  396. $promise->then(function ($v) use (&$r): void { $r = $v; });
  397. $this->assertSame(999, $promise->wait());
  398. $this->assertSame(999, $r);
  399. }
  400. private function createLotsOfFlappingPromise()
  401. {
  402. return P\Coroutine::of(function () {
  403. $value = 0;
  404. for ($i = 0; $i < 1000; ++$i) {
  405. try {
  406. if ($i % 2) {
  407. $value = (yield new FulfilledPromise($i));
  408. } else {
  409. $value = (yield new RejectedPromise($i));
  410. }
  411. } catch (\Exception $e) {
  412. $value = (yield new FulfilledPromise($i));
  413. }
  414. }
  415. yield $value;
  416. });
  417. }
  418. public function testLotsOfTryCatchingDoesNotBlowStack(): void
  419. {
  420. if (defined('HHVM_VERSION')) {
  421. $this->markTestIncomplete('Broken on HHVM.');
  422. }
  423. $promise = $this->createLotsOfFlappingPromise();
  424. $promise->then(function ($v) use (&$r): void { $r = $v; });
  425. P\Utils::queue()->run();
  426. $this->assertSame(999, $r);
  427. }
  428. public function testLotsOfTryCatchingWaitingDoesNotBlowStack(): void
  429. {
  430. if (defined('HHVM_VERSION')) {
  431. $this->markTestIncomplete('Broken on HHVM.');
  432. }
  433. $promise = $this->createLotsOfFlappingPromise();
  434. $promise->then(function ($v) use (&$r): void { $r = $v; });
  435. $this->assertSame(999, $promise->wait());
  436. $this->assertSame(999, $r);
  437. }
  438. public function testAsyncPromisesWithCorrectlyYieldedValues(): void
  439. {
  440. if (defined('HHVM_VERSION')) {
  441. $this->markTestIncomplete('Broken on HHVM.');
  442. }
  443. $promises = [
  444. new Promise(),
  445. new Promise(),
  446. new Promise(),
  447. ];
  448. eval('
  449. $promise = \GuzzleHttp\Promise\Coroutine::of(function () use ($promises) {
  450. $value = null;
  451. $this->assertSame(\'skip\', (yield new \GuzzleHttp\Promise\FulfilledPromise(\'skip\')));
  452. foreach ($promises as $idx => $p) {
  453. $value = (yield $p);
  454. $this->assertSame($idx, $value);
  455. $this->assertSame(\'skip\', (yield new \GuzzleHttp\Promise\FulfilledPromise(\'skip\')));
  456. }
  457. $this->assertSame(\'skip\', (yield new \GuzzleHttp\Promise\FulfilledPromise(\'skip\')));
  458. yield $value;
  459. });
  460. ');
  461. $promises[0]->resolve(0);
  462. $promises[1]->resolve(1);
  463. $promises[2]->resolve(2);
  464. $promise->then(function ($v) use (&$r): void { $r = $v; });
  465. P\Utils::queue()->run();
  466. $this->assertSame(2, $r);
  467. }
  468. public function testYieldFinalWaitablePromise(): void
  469. {
  470. if (defined('HHVM_VERSION')) {
  471. $this->markTestIncomplete('Broken on HHVM.');
  472. }
  473. $p1 = new Promise(function () use (&$p1): void {
  474. $p1->resolve('skip me');
  475. });
  476. $p2 = new Promise(function () use (&$p2): void {
  477. $p2->resolve('hello!');
  478. });
  479. $co = P\Coroutine::of(function () use ($p1, $p2) {
  480. yield $p1;
  481. yield $p2;
  482. });
  483. P\Utils::queue()->run();
  484. $this->assertSame('hello!', $co->wait());
  485. }
  486. public function testCanYieldFinalPendingPromise(): void
  487. {
  488. if (defined('HHVM_VERSION')) {
  489. $this->markTestIncomplete('Broken on HHVM.');
  490. }
  491. $p1 = new Promise();
  492. $p2 = new Promise();
  493. $co = P\Coroutine::of(function () use ($p1, $p2) {
  494. yield $p1;
  495. yield $p2;
  496. });
  497. $p1->resolve('a');
  498. $p2->resolve('b');
  499. $co->then(function ($value) use (&$result): void { $result = $value; });
  500. P\Utils::queue()->run();
  501. $this->assertSame('b', $result);
  502. }
  503. public function testCanNestYieldsAndFailures(): void
  504. {
  505. if (defined('HHVM_VERSION')) {
  506. $this->markTestIncomplete('Broken on HHVM.');
  507. }
  508. $p1 = new Promise();
  509. $p2 = new Promise();
  510. $p3 = new Promise();
  511. $p4 = new Promise();
  512. $p5 = new Promise();
  513. $co = P\Coroutine::of(function () use ($p1, $p2, $p3, $p4, $p5) {
  514. try {
  515. yield $p1;
  516. } catch (\Exception $e) {
  517. yield $p2;
  518. try {
  519. yield $p3;
  520. yield $p4;
  521. } catch (\Exception $e) {
  522. yield $p5;
  523. }
  524. }
  525. });
  526. $p1->reject('a');
  527. $p2->resolve('b');
  528. $p3->resolve('c');
  529. $p4->reject('d');
  530. $p5->resolve('e');
  531. $co->then(function ($value) use (&$result): void { $result = $value; });
  532. P\Utils::queue()->run();
  533. $this->assertSame('e', $result);
  534. }
  535. public function testCanYieldErrorsAndSuccessesWithoutRecursion(): void
  536. {
  537. if (defined('HHVM_VERSION')) {
  538. $this->markTestIncomplete('Broken on HHVM.');
  539. }
  540. $promises = [];
  541. for ($i = 0; $i < 20; ++$i) {
  542. $promises[] = new Promise();
  543. }
  544. $co = P\Coroutine::of(function () use ($promises) {
  545. for ($i = 0; $i < 20; $i += 4) {
  546. try {
  547. yield $promises[$i];
  548. yield $promises[$i + 1];
  549. } catch (\Exception $e) {
  550. yield $promises[$i + 2];
  551. yield $promises[$i + 3];
  552. }
  553. }
  554. });
  555. for ($i = 0; $i < 20; $i += 4) {
  556. $promises[$i]->resolve($i);
  557. $promises[$i + 1]->reject($i + 1);
  558. $promises[$i + 2]->resolve($i + 2);
  559. $promises[$i + 3]->resolve($i + 3);
  560. }
  561. $co->then(function ($value) use (&$result): void { $result = $value; });
  562. P\Utils::queue()->run();
  563. $this->assertSame(19, $result);
  564. }
  565. public function testCanWaitOnPromiseAfterFulfilled(): void
  566. {
  567. if (defined('HHVM_VERSION')) {
  568. $this->markTestIncomplete('Broken on HHVM.');
  569. }
  570. $f = function () {
  571. static $i = 0;
  572. ++$i;
  573. return $p = new Promise(function () use (&$p, $i): void {
  574. $p->resolve($i.'-bar');
  575. });
  576. };
  577. $promises = [];
  578. for ($i = 0; $i < 20; ++$i) {
  579. $promises[] = $f();
  580. }
  581. $p = P\Coroutine::of(function () use ($promises) {
  582. yield new FulfilledPromise('foo!');
  583. foreach ($promises as $promise) {
  584. yield $promise;
  585. }
  586. });
  587. $this->assertSame('20-bar', $p->wait());
  588. }
  589. public function testCanWaitOnErroredPromises(): void
  590. {
  591. if (defined('HHVM_VERSION')) {
  592. $this->markTestIncomplete('Broken on HHVM.');
  593. }
  594. $p1 = new Promise(function () use (&$p1): void { $p1->reject('a'); });
  595. $p2 = new Promise(function () use (&$p2): void { $p2->resolve('b'); });
  596. $p3 = new Promise(function () use (&$p3): void { $p3->resolve('c'); });
  597. $p4 = new Promise(function () use (&$p4): void { $p4->reject('d'); });
  598. $p5 = new Promise(function () use (&$p5): void { $p5->resolve('e'); });
  599. $p6 = new Promise(function () use (&$p6): void { $p6->reject('f'); });
  600. $co = P\Coroutine::of(function () use ($p1, $p2, $p3, $p4, $p5, $p6) {
  601. try {
  602. yield $p1;
  603. } catch (\Exception $e) {
  604. yield $p2;
  605. try {
  606. yield $p3;
  607. yield $p4;
  608. } catch (\Exception $e) {
  609. yield $p5;
  610. yield $p6;
  611. }
  612. }
  613. });
  614. $res = P\Utils::inspect($co);
  615. $this->assertSame('f', $res['reason']);
  616. }
  617. public function testCoroutineOtherwiseIntegrationTest(): void
  618. {
  619. if (defined('HHVM_VERSION')) {
  620. $this->markTestIncomplete('Broken on HHVM.');
  621. }
  622. $a = new Promise();
  623. $b = new Promise();
  624. $promise = P\Coroutine::of(function () use ($a, $b) {
  625. // Execute the pool of commands concurrently, and process errors.
  626. yield $a;
  627. yield $b;
  628. })->otherwise(function (\Exception $e): void {
  629. // Throw errors from the operations as a specific Multipart error.
  630. throw new \OutOfBoundsException('a', 0, $e);
  631. });
  632. $a->resolve('a');
  633. $b->reject('b');
  634. $reason = P\Utils::inspect($promise)['reason'];
  635. $this->assertInstanceOf(\OutOfBoundsException::class, $reason);
  636. $this->assertInstanceOf(RejectionException::class, $reason->getPrevious());
  637. }
  638. public function testCanManuallySettleTaskQueueGeneratedPromises(): void
  639. {
  640. $p1 = P\Utils::task(function () { return 'a'; });
  641. $p2 = P\Utils::task(function () { return 'b'; });
  642. $p3 = P\Utils::task(function () { return 'c'; });
  643. $p1->cancel();
  644. $p2->resolve('b2');
  645. $results = P\Utils::inspectAll([$p1, $p2, $p3]);
  646. $this->assertSame([
  647. ['state' => 'rejected', 'reason' => 'Promise has been cancelled'],
  648. ['state' => 'fulfilled', 'value' => 'b2'],
  649. ['state' => 'fulfilled', 'value' => 'c'],
  650. ], $results);
  651. }
  652. }