1<?php 2 3namespace React\Promise; 4 5class FunctionReduceTest extends TestCase 6{ 7 protected function plus() 8 { 9 return function ($sum, $val) { 10 return $sum + $val; 11 }; 12 } 13 14 protected function append() 15 { 16 return function ($sum, $val) { 17 return $sum . $val; 18 }; 19 } 20 21 /** @test */ 22 public function shouldReduceValuesWithoutInitialValue() 23 { 24 $mock = $this->createCallableMock(); 25 $mock 26 ->expects($this->once()) 27 ->method('__invoke') 28 ->with($this->identicalTo(6)); 29 30 reduce( 31 [1, 2, 3], 32 $this->plus() 33 )->then($mock); 34 } 35 36 /** @test */ 37 public function shouldReduceValuesWithInitialValue() 38 { 39 $mock = $this->createCallableMock(); 40 $mock 41 ->expects($this->once()) 42 ->method('__invoke') 43 ->with($this->identicalTo(7)); 44 45 reduce( 46 [1, 2, 3], 47 $this->plus(), 48 1 49 )->then($mock); 50 } 51 52 /** @test */ 53 public function shouldReduceValuesWithInitialPromise() 54 { 55 $mock = $this->createCallableMock(); 56 $mock 57 ->expects($this->once()) 58 ->method('__invoke') 59 ->with($this->identicalTo(7)); 60 61 reduce( 62 [1, 2, 3], 63 $this->plus(), 64 resolve(1) 65 )->then($mock); 66 } 67 68 /** @test */ 69 public function shouldReducePromisedValuesWithoutInitialValue() 70 { 71 $mock = $this->createCallableMock(); 72 $mock 73 ->expects($this->once()) 74 ->method('__invoke') 75 ->with($this->identicalTo(6)); 76 77 reduce( 78 [resolve(1), resolve(2), resolve(3)], 79 $this->plus() 80 )->then($mock); 81 } 82 83 /** @test */ 84 public function shouldReducePromisedValuesWithInitialValue() 85 { 86 $mock = $this->createCallableMock(); 87 $mock 88 ->expects($this->once()) 89 ->method('__invoke') 90 ->with($this->identicalTo(7)); 91 92 reduce( 93 [resolve(1), resolve(2), resolve(3)], 94 $this->plus(), 95 1 96 )->then($mock); 97 } 98 99 /** @test */ 100 public function shouldReducePromisedValuesWithInitialPromise() 101 { 102 $mock = $this->createCallableMock(); 103 $mock 104 ->expects($this->once()) 105 ->method('__invoke') 106 ->with($this->identicalTo(7)); 107 108 reduce( 109 [resolve(1), resolve(2), resolve(3)], 110 $this->plus(), 111 resolve(1) 112 )->then($mock); 113 } 114 115 /** @test */ 116 public function shouldReduceEmptyInputWithInitialValue() 117 { 118 $mock = $this->createCallableMock(); 119 $mock 120 ->expects($this->once()) 121 ->method('__invoke') 122 ->with($this->identicalTo(1)); 123 124 reduce( 125 [], 126 $this->plus(), 127 1 128 )->then($mock); 129 } 130 131 /** @test */ 132 public function shouldReduceEmptyInputWithInitialPromise() 133 { 134 $mock = $this->createCallableMock(); 135 $mock 136 ->expects($this->once()) 137 ->method('__invoke') 138 ->with($this->identicalTo(1)); 139 140 reduce( 141 [], 142 $this->plus(), 143 resolve(1) 144 )->then($mock); 145 } 146 147 /** @test */ 148 public function shouldRejectWhenInputContainsRejection() 149 { 150 $mock = $this->createCallableMock(); 151 $mock 152 ->expects($this->once()) 153 ->method('__invoke') 154 ->with($this->identicalTo(2)); 155 156 reduce( 157 [resolve(1), reject(2), resolve(3)], 158 $this->plus(), 159 resolve(1) 160 )->then($this->expectCallableNever(), $mock); 161 } 162 163 /** @test */ 164 public function shouldResolveWithNullWhenInputIsEmptyAndNoInitialValueOrPromiseProvided() 165 { 166 // Note: this is different from when.js's behavior! 167 // In when.reduce(), this rejects with a TypeError exception (following 168 // JavaScript's [].reduce behavior. 169 // We're following PHP's array_reduce behavior and resolve with NULL. 170 $mock = $this->createCallableMock(); 171 $mock 172 ->expects($this->once()) 173 ->method('__invoke') 174 ->with($this->identicalTo(null)); 175 176 reduce( 177 [], 178 $this->plus() 179 )->then($mock); 180 } 181 182 /** @test */ 183 public function shouldAllowSparseArrayInputWithoutInitialValue() 184 { 185 $mock = $this->createCallableMock(); 186 $mock 187 ->expects($this->once()) 188 ->method('__invoke') 189 ->with($this->identicalTo(3)); 190 191 reduce( 192 [null, null, 1, null, 1, 1], 193 $this->plus() 194 )->then($mock); 195 } 196 197 /** @test */ 198 public function shouldAllowSparseArrayInputWithInitialValue() 199 { 200 $mock = $this->createCallableMock(); 201 $mock 202 ->expects($this->once()) 203 ->method('__invoke') 204 ->with($this->identicalTo(4)); 205 206 reduce( 207 [null, null, 1, null, 1, 1], 208 $this->plus(), 209 1 210 )->then($mock); 211 } 212 213 /** @test */ 214 public function shouldReduceInInputOrder() 215 { 216 $mock = $this->createCallableMock(); 217 $mock 218 ->expects($this->once()) 219 ->method('__invoke') 220 ->with($this->identicalTo('123')); 221 222 reduce( 223 [1, 2, 3], 224 $this->append(), 225 '' 226 )->then($mock); 227 } 228 229 /** @test */ 230 public function shouldAcceptAPromiseForAnArray() 231 { 232 $mock = $this->createCallableMock(); 233 $mock 234 ->expects($this->once()) 235 ->method('__invoke') 236 ->with($this->identicalTo('123')); 237 238 reduce( 239 resolve([1, 2, 3]), 240 $this->append(), 241 '' 242 )->then($mock); 243 } 244 245 /** @test */ 246 public function shouldResolveToInitialValueWhenInputPromiseDoesNotResolveToAnArray() 247 { 248 $mock = $this->createCallableMock(); 249 $mock 250 ->expects($this->once()) 251 ->method('__invoke') 252 ->with($this->identicalTo(1)); 253 254 reduce( 255 resolve(1), 256 $this->plus(), 257 1 258 )->then($mock); 259 } 260 261 /** @test */ 262 public function shouldProvideCorrectBasisValue() 263 { 264 $insertIntoArray = function ($arr, $val, $i) { 265 $arr[$i] = $val; 266 267 return $arr; 268 }; 269 270 $d1 = new Deferred(); 271 $d2 = new Deferred(); 272 $d3 = new Deferred(); 273 274 $mock = $this->createCallableMock(); 275 $mock 276 ->expects($this->once()) 277 ->method('__invoke') 278 ->with($this->identicalTo([1, 2, 3])); 279 280 reduce( 281 [$d1->promise(), $d2->promise(), $d3->promise()], 282 $insertIntoArray, 283 [] 284 )->then($mock); 285 286 $d3->resolve(3); 287 $d1->resolve(1); 288 $d2->resolve(2); 289 } 290 291 /** @test */ 292 public function shouldRejectWhenInputPromiseRejects() 293 { 294 $mock = $this->createCallableMock(); 295 $mock 296 ->expects($this->once()) 297 ->method('__invoke') 298 ->with($this->identicalTo(null)); 299 300 reduce( 301 reject(), 302 $this->plus(), 303 1 304 )->then($this->expectCallableNever(), $mock); 305 } 306 307 /** @test */ 308 public function shouldCancelInputPromise() 309 { 310 $mock = $this 311 ->getMockBuilder('React\Promise\CancellablePromiseInterface') 312 ->getMock(); 313 $mock 314 ->expects($this->once()) 315 ->method('cancel'); 316 317 reduce( 318 $mock, 319 $this->plus(), 320 1 321 )->cancel(); 322 } 323 324 /** @test */ 325 public function shouldCancelInputArrayPromises() 326 { 327 $mock1 = $this 328 ->getMockBuilder('React\Promise\CancellablePromiseInterface') 329 ->getMock(); 330 $mock1 331 ->expects($this->once()) 332 ->method('cancel'); 333 334 $mock2 = $this 335 ->getMockBuilder('React\Promise\CancellablePromiseInterface') 336 ->getMock(); 337 $mock2 338 ->expects($this->once()) 339 ->method('cancel'); 340 341 reduce( 342 [$mock1, $mock2], 343 $this->plus(), 344 1 345 )->cancel(); 346 } 347} 348