PageRenderTime 37ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/guzzlehttp/promises/tests/EachPromiseTest.php

https://gitlab.com/ealexis.t/trends
PHP | 336 lines | 306 code | 22 blank | 8 comment | 9 complexity | b318d05d24f3af117a5cd50eb89a714f MD5 | raw file
  1. <?php
  2. namespace GuzzleHttp\Promise\Tests;
  3. use GuzzleHttp\Promise\RejectedPromise;
  4. use GuzzleHttp\Promise\FulfilledPromise;
  5. use GuzzleHttp\Promise\Promise;
  6. use GuzzleHttp\Promise\PromiseInterface;
  7. use GuzzleHttp\Promise\EachPromise;
  8. use GuzzleHttp\Promise as P;
  9. /**
  10. * @covers GuzzleHttp\Promise\EachPromise
  11. */
  12. class EachPromiseTest extends \PHPUnit_Framework_TestCase
  13. {
  14. public function testReturnsSameInstance()
  15. {
  16. $each = new EachPromise([], ['concurrency' => 100]);
  17. $this->assertSame($each->promise(), $each->promise());
  18. }
  19. public function testInvokesAllPromises()
  20. {
  21. $promises = [new Promise(), new Promise(), new Promise()];
  22. $called = [];
  23. $each = new EachPromise($promises, [
  24. 'fulfilled' => function ($value) use (&$called) {
  25. $called[] = $value;
  26. }
  27. ]);
  28. $p = $each->promise();
  29. $promises[0]->resolve('a');
  30. $promises[1]->resolve('c');
  31. $promises[2]->resolve('b');
  32. P\queue()->run();
  33. $this->assertEquals(['a', 'c', 'b'], $called);
  34. $this->assertEquals(PromiseInterface::FULFILLED, $p->getState());
  35. }
  36. public function testIsWaitable()
  37. {
  38. $a = $this->createSelfResolvingPromise('a');
  39. $b = $this->createSelfResolvingPromise('b');
  40. $called = [];
  41. $each = new EachPromise([$a, $b], [
  42. 'fulfilled' => function ($value) use (&$called) { $called[] = $value; }
  43. ]);
  44. $p = $each->promise();
  45. $this->assertNull($p->wait());
  46. $this->assertEquals(PromiseInterface::FULFILLED, $p->getState());
  47. $this->assertEquals(['a', 'b'], $called);
  48. }
  49. public function testCanResolveBeforeConsumingAll()
  50. {
  51. $called = 0;
  52. $a = $this->createSelfResolvingPromise('a');
  53. $b = new Promise(function () { $this->fail(); });
  54. $each = new EachPromise([$a, $b], [
  55. 'fulfilled' => function ($value, $idx, Promise $aggregate) use (&$called) {
  56. $this->assertSame($idx, 0);
  57. $this->assertEquals('a', $value);
  58. $aggregate->resolve(null);
  59. $called++;
  60. },
  61. 'rejected' => function (\Exception $reason) {
  62. $this->fail($reason->getMessage());
  63. }
  64. ]);
  65. $p = $each->promise();
  66. $p->wait();
  67. $this->assertNull($p->wait());
  68. $this->assertEquals(1, $called);
  69. $this->assertEquals(PromiseInterface::FULFILLED, $a->getState());
  70. $this->assertEquals(PromiseInterface::PENDING, $b->getState());
  71. // Resolving $b has no effect on the aggregate promise.
  72. $b->resolve('foo');
  73. $this->assertEquals(1, $called);
  74. }
  75. public function testLimitsPendingPromises()
  76. {
  77. $pending = [new Promise(), new Promise(), new Promise(), new Promise()];
  78. $promises = new \ArrayIterator($pending);
  79. $each = new EachPromise($promises, ['concurrency' => 2]);
  80. $p = $each->promise();
  81. $this->assertCount(2, $this->readAttribute($each, 'pending'));
  82. $pending[0]->resolve('a');
  83. $this->assertCount(2, $this->readAttribute($each, 'pending'));
  84. $this->assertTrue($promises->valid());
  85. $pending[1]->resolve('b');
  86. P\queue()->run();
  87. $this->assertCount(2, $this->readAttribute($each, 'pending'));
  88. $this->assertTrue($promises->valid());
  89. $promises[2]->resolve('c');
  90. P\queue()->run();
  91. $this->assertCount(1, $this->readAttribute($each, 'pending'));
  92. $this->assertEquals(PromiseInterface::PENDING, $p->getState());
  93. $promises[3]->resolve('d');
  94. P\queue()->run();
  95. $this->assertNull($this->readAttribute($each, 'pending'));
  96. $this->assertEquals(PromiseInterface::FULFILLED, $p->getState());
  97. $this->assertFalse($promises->valid());
  98. }
  99. public function testDynamicallyLimitsPendingPromises()
  100. {
  101. $calls = [];
  102. $pendingFn = function ($count) use (&$calls) {
  103. $calls[] = $count;
  104. return 2;
  105. };
  106. $pending = [new Promise(), new Promise(), new Promise(), new Promise()];
  107. $promises = new \ArrayIterator($pending);
  108. $each = new EachPromise($promises, ['concurrency' => $pendingFn]);
  109. $p = $each->promise();
  110. $this->assertCount(2, $this->readAttribute($each, 'pending'));
  111. $pending[0]->resolve('a');
  112. $this->assertCount(2, $this->readAttribute($each, 'pending'));
  113. $this->assertTrue($promises->valid());
  114. $pending[1]->resolve('b');
  115. $this->assertCount(2, $this->readAttribute($each, 'pending'));
  116. P\queue()->run();
  117. $this->assertTrue($promises->valid());
  118. $promises[2]->resolve('c');
  119. P\queue()->run();
  120. $this->assertCount(1, $this->readAttribute($each, 'pending'));
  121. $this->assertEquals(PromiseInterface::PENDING, $p->getState());
  122. $promises[3]->resolve('d');
  123. P\queue()->run();
  124. $this->assertNull($this->readAttribute($each, 'pending'));
  125. $this->assertEquals(PromiseInterface::FULFILLED, $p->getState());
  126. $this->assertEquals([0, 1, 1, 1], $calls);
  127. $this->assertFalse($promises->valid());
  128. }
  129. public function testClearsReferencesWhenResolved()
  130. {
  131. $called = false;
  132. $a = new Promise(function () use (&$a, &$called) {
  133. $a->resolve('a');
  134. $called = true;
  135. });
  136. $each = new EachPromise([$a], [
  137. 'concurrency' => function () { return 1; },
  138. 'fulfilled' => function () {},
  139. 'rejected' => function () {}
  140. ]);
  141. $each->promise()->wait();
  142. $this->assertNull($this->readAttribute($each, 'onFulfilled'));
  143. $this->assertNull($this->readAttribute($each, 'onRejected'));
  144. $this->assertNull($this->readAttribute($each, 'iterable'));
  145. $this->assertNull($this->readAttribute($each, 'pending'));
  146. $this->assertNull($this->readAttribute($each, 'concurrency'));
  147. $this->assertTrue($called);
  148. }
  149. public function testCanBeCancelled()
  150. {
  151. $this->markTestIncomplete();
  152. }
  153. public function testFulfillsImmediatelyWhenGivenAnEmptyIterator()
  154. {
  155. $each = new EachPromise(new \ArrayIterator([]));
  156. $result = $each->promise()->wait();
  157. }
  158. public function testDoesNotBlowStackWithFulfilledPromises()
  159. {
  160. $pending = [];
  161. for ($i = 0; $i < 100; $i++) {
  162. $pending[] = new FulfilledPromise($i);
  163. }
  164. $values = [];
  165. $each = new EachPromise($pending, [
  166. 'fulfilled' => function ($value) use (&$values) {
  167. $values[] = $value;
  168. }
  169. ]);
  170. $called = false;
  171. $each->promise()->then(function () use (&$called) {
  172. $called = true;
  173. });
  174. $this->assertFalse($called);
  175. P\queue()->run();
  176. $this->assertTrue($called);
  177. $this->assertEquals(range(0, 99), $values);
  178. }
  179. public function testDoesNotBlowStackWithRejectedPromises()
  180. {
  181. $pending = [];
  182. for ($i = 0; $i < 100; $i++) {
  183. $pending[] = new RejectedPromise($i);
  184. }
  185. $values = [];
  186. $each = new EachPromise($pending, [
  187. 'rejected' => function ($value) use (&$values) {
  188. $values[] = $value;
  189. }
  190. ]);
  191. $called = false;
  192. $each->promise()->then(
  193. function () use (&$called) { $called = true; },
  194. function () { $this->fail('Should not have rejected.'); }
  195. );
  196. $this->assertFalse($called);
  197. P\queue()->run();
  198. $this->assertTrue($called);
  199. $this->assertEquals(range(0, 99), $values);
  200. }
  201. public function testReturnsPromiseForWhatever()
  202. {
  203. $called = [];
  204. $arr = ['a', 'b'];
  205. $each = new EachPromise($arr, [
  206. 'fulfilled' => function ($v) use (&$called) { $called[] = $v; }
  207. ]);
  208. $p = $each->promise();
  209. $this->assertNull($p->wait());
  210. $this->assertEquals(['a', 'b'], $called);
  211. }
  212. public function testRejectsAggregateWhenNextThrows()
  213. {
  214. $iter = function () {
  215. yield 'a';
  216. throw new \Exception('Failure');
  217. };
  218. $each = new EachPromise($iter());
  219. $p = $each->promise();
  220. $e = null;
  221. $received = null;
  222. $p->then(null, function ($reason) use (&$e) { $e = $reason; });
  223. P\queue()->run();
  224. $this->assertInstanceOf('Exception', $e);
  225. $this->assertEquals('Failure', $e->getMessage());
  226. }
  227. public function testDoesNotCallNextOnIteratorUntilNeededWhenWaiting()
  228. {
  229. $results = [];
  230. $values = [10];
  231. $remaining = 9;
  232. $iter = function () use (&$values) {
  233. while ($value = array_pop($values)) {
  234. yield $value;
  235. }
  236. };
  237. $each = new EachPromise($iter(), [
  238. 'concurrency' => 1,
  239. 'fulfilled' => function ($r) use (&$results, &$values, &$remaining) {
  240. $results[] = $r;
  241. if ($remaining > 0) {
  242. $values[] = $remaining--;
  243. }
  244. }
  245. ]);
  246. $each->promise()->wait();
  247. $this->assertEquals(range(10, 1), $results);
  248. }
  249. public function testDoesNotCallNextOnIteratorUntilNeededWhenAsync()
  250. {
  251. $firstPromise = new Promise();
  252. $pending = [$firstPromise];
  253. $values = [$firstPromise];
  254. $results = [];
  255. $remaining = 9;
  256. $iter = function () use (&$values) {
  257. while ($value = array_pop($values)) {
  258. yield $value;
  259. }
  260. };
  261. $each = new EachPromise($iter(), [
  262. 'concurrency' => 1,
  263. 'fulfilled' => function ($r) use (&$results, &$values, &$remaining, &$pending) {
  264. $results[] = $r;
  265. if ($remaining-- > 0) {
  266. $pending[] = $values[] = new Promise();
  267. }
  268. }
  269. ]);
  270. $i = 0;
  271. $each->promise();
  272. while ($promise = array_pop($pending)) {
  273. $promise->resolve($i++);
  274. P\queue()->run();
  275. }
  276. $this->assertEquals(range(0, 9), $results);
  277. }
  278. private function createSelfResolvingPromise($value)
  279. {
  280. $p = new Promise(function () use (&$p, $value) {
  281. $p->resolve($value);
  282. });
  283. return $p;
  284. }
  285. public function testMutexPreventsGeneratorRecursion()
  286. {
  287. $results = $promises = [];
  288. for ($i = 0; $i < 20; $i++) {
  289. $p = $this->createSelfResolvingPromise($i);
  290. $pending[] = $p;
  291. $promises[] = $p;
  292. }
  293. $iter = function () use (&$promises, &$pending) {
  294. foreach ($promises as $promise) {
  295. // Resolve a promises, which will trigger the then() function,
  296. // which would cause the EachPromise to try to add more
  297. // promises to the queue. Without a lock, this would trigger
  298. // a "Cannot resume an already running generator" fatal error.
  299. if ($p = array_pop($pending)) {
  300. $p->wait();
  301. }
  302. yield $promise;
  303. }
  304. };
  305. $each = new EachPromise($iter(), [
  306. 'concurrency' => 5,
  307. 'fulfilled' => function ($r) use (&$results, &$pending) {
  308. $results[] = $r;
  309. }
  310. ]);
  311. $each->promise()->wait();
  312. $this->assertCount(20, $results);
  313. }
  314. }