PluginProbe
StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more / 2.6.1
StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more v2.6.1
3.7.2 3.7.1 3.7.0 3.6.0 3.6.1 3.5.2 trunk 2.0 2.1 2.2 2.3 2.4 2.4.1 2.4.2 2.4.3 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.6.0 2.6.1 All 73 releases
stockpack / vendor / guzzlehttp / promises / tests / EachPromiseTest.php

EachPromiseTest.php in StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more 2.6.1, at vendor/guzzlehttp/promises/tests/EachPromiseTest.php

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