OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:async'; |
| 6 |
| 7 import 'package:fake_async/fake_async.dart'; |
| 8 import 'package:pool/pool.dart'; |
| 9 import 'package:stack_trace/stack_trace.dart'; |
| 10 import 'package:test/test.dart'; |
| 11 |
| 12 void main() { |
| 13 group("request()", () { |
| 14 test("resources can be requested freely up to the limit", () { |
| 15 var pool = new Pool(50); |
| 16 for (var i = 0; i < 50; i++) { |
| 17 expect(pool.request(), completes); |
| 18 } |
| 19 }); |
| 20 |
| 21 test("resources block past the limit", () { |
| 22 new FakeAsync().run((async) { |
| 23 var pool = new Pool(50); |
| 24 for (var i = 0; i < 50; i++) { |
| 25 expect(pool.request(), completes); |
| 26 } |
| 27 expect(pool.request(), doesNotComplete); |
| 28 |
| 29 async.elapse(new Duration(seconds: 1)); |
| 30 }); |
| 31 }); |
| 32 |
| 33 test("a blocked resource is allocated when another is released", () { |
| 34 new FakeAsync().run((async) { |
| 35 var pool = new Pool(50); |
| 36 for (var i = 0; i < 49; i++) { |
| 37 expect(pool.request(), completes); |
| 38 } |
| 39 |
| 40 pool.request().then((lastAllocatedResource) { |
| 41 // This will only complete once [lastAllocatedResource] is released. |
| 42 expect(pool.request(), completes); |
| 43 |
| 44 new Future.delayed(new Duration(microseconds: 1)).then((_) { |
| 45 lastAllocatedResource.release(); |
| 46 }); |
| 47 }); |
| 48 |
| 49 async.elapse(new Duration(seconds: 1)); |
| 50 }); |
| 51 }); |
| 52 }); |
| 53 |
| 54 group("withResource()", () { |
| 55 test("can be called freely up to the limit", () { |
| 56 var pool = new Pool(50); |
| 57 for (var i = 0; i < 50; i++) { |
| 58 pool.withResource(expectAsync(() => new Completer().future)); |
| 59 } |
| 60 }); |
| 61 |
| 62 test("blocks the callback past the limit", () { |
| 63 new FakeAsync().run((async) { |
| 64 var pool = new Pool(50); |
| 65 for (var i = 0; i < 50; i++) { |
| 66 pool.withResource(expectAsync(() => new Completer().future)); |
| 67 } |
| 68 pool.withResource(expectNoAsync()); |
| 69 |
| 70 async.elapse(new Duration(seconds: 1)); |
| 71 }); |
| 72 }); |
| 73 |
| 74 test("a blocked resource is allocated when another is released", () { |
| 75 new FakeAsync().run((async) { |
| 76 var pool = new Pool(50); |
| 77 for (var i = 0; i < 49; i++) { |
| 78 pool.withResource(expectAsync(() => new Completer().future)); |
| 79 } |
| 80 |
| 81 var completer = new Completer(); |
| 82 pool.withResource(() => completer.future); |
| 83 var blockedResourceAllocated = false; |
| 84 pool.withResource(() { |
| 85 blockedResourceAllocated = true; |
| 86 }); |
| 87 |
| 88 new Future.delayed(new Duration(microseconds: 1)).then((_) { |
| 89 expect(blockedResourceAllocated, isFalse); |
| 90 completer.complete(); |
| 91 return new Future.delayed(new Duration(microseconds: 1)); |
| 92 }).then((_) { |
| 93 expect(blockedResourceAllocated, isTrue); |
| 94 }); |
| 95 |
| 96 async.elapse(new Duration(seconds: 1)); |
| 97 }); |
| 98 }); |
| 99 }); |
| 100 |
| 101 group("with a timeout", () { |
| 102 test("doesn't time out if there are no pending requests", () { |
| 103 new FakeAsync().run((async) { |
| 104 var pool = new Pool(50, timeout: new Duration(seconds: 5)); |
| 105 for (var i = 0; i < 50; i++) { |
| 106 expect(pool.request(), completes); |
| 107 } |
| 108 |
| 109 async.elapse(new Duration(seconds: 6)); |
| 110 }); |
| 111 }); |
| 112 |
| 113 test("resets the timer if a resource is returned", () { |
| 114 new FakeAsync().run((async) { |
| 115 var pool = new Pool(50, timeout: new Duration(seconds: 5)); |
| 116 for (var i = 0; i < 49; i++) { |
| 117 expect(pool.request(), completes); |
| 118 } |
| 119 |
| 120 pool.request().then((lastAllocatedResource) { |
| 121 // This will only complete once [lastAllocatedResource] is released. |
| 122 expect(pool.request(), completes); |
| 123 |
| 124 new Future.delayed(new Duration(seconds: 3)).then((_) { |
| 125 lastAllocatedResource.release(); |
| 126 expect(pool.request(), doesNotComplete); |
| 127 }); |
| 128 }); |
| 129 |
| 130 async.elapse(new Duration(seconds: 6)); |
| 131 }); |
| 132 }); |
| 133 |
| 134 test("resets the timer if a resource is requested", () { |
| 135 new FakeAsync().run((async) { |
| 136 var pool = new Pool(50, timeout: new Duration(seconds: 5)); |
| 137 for (var i = 0; i < 50; i++) { |
| 138 expect(pool.request(), completes); |
| 139 } |
| 140 expect(pool.request(), doesNotComplete); |
| 141 |
| 142 new Future.delayed(new Duration(seconds: 3)).then((_) { |
| 143 expect(pool.request(), doesNotComplete); |
| 144 }); |
| 145 |
| 146 async.elapse(new Duration(seconds: 6)); |
| 147 }); |
| 148 }); |
| 149 |
| 150 test("times out if nothing happens", () { |
| 151 new FakeAsync().run((async) { |
| 152 var pool = new Pool(50, timeout: new Duration(seconds: 5)); |
| 153 for (var i = 0; i < 50; i++) { |
| 154 expect(pool.request(), completes); |
| 155 } |
| 156 expect(pool.request(), throwsA(new isInstanceOf<TimeoutException>())); |
| 157 |
| 158 async.elapse(new Duration(seconds: 6)); |
| 159 }); |
| 160 }); |
| 161 }); |
| 162 |
| 163 group("allowRelease()", () { |
| 164 test("runs the callback once the resource limit is exceeded", () async { |
| 165 var pool = new Pool(50); |
| 166 for (var i = 0; i < 49; i++) { |
| 167 expect(pool.request(), completes); |
| 168 } |
| 169 |
| 170 var resource = await pool.request(); |
| 171 var onReleaseCalled = false; |
| 172 resource.allowRelease(() => onReleaseCalled = true); |
| 173 await new Future.delayed(Duration.ZERO); |
| 174 expect(onReleaseCalled, isFalse); |
| 175 |
| 176 expect(pool.request(), completes); |
| 177 await new Future.delayed(Duration.ZERO); |
| 178 expect(onReleaseCalled, isTrue); |
| 179 }); |
| 180 |
| 181 test("runs the callback immediately if there are blocked requests", |
| 182 () async { |
| 183 var pool = new Pool(1); |
| 184 var resource = await pool.request(); |
| 185 |
| 186 // This will be blocked until [resource.allowRelease] is called. |
| 187 expect(pool.request(), completes); |
| 188 |
| 189 var onReleaseCalled = false; |
| 190 resource.allowRelease(() => onReleaseCalled = true); |
| 191 await new Future.delayed(Duration.ZERO); |
| 192 expect(onReleaseCalled, isTrue); |
| 193 }); |
| 194 |
| 195 test("blocks the request until the callback completes", () async { |
| 196 var pool = new Pool(1); |
| 197 var resource = await pool.request(); |
| 198 |
| 199 var requestComplete = false; |
| 200 pool.request().then((_) => requestComplete = true); |
| 201 |
| 202 var completer = new Completer(); |
| 203 resource.allowRelease(() => completer.future); |
| 204 await new Future.delayed(Duration.ZERO); |
| 205 expect(requestComplete, isFalse); |
| 206 |
| 207 completer.complete(); |
| 208 await new Future.delayed(Duration.ZERO); |
| 209 expect(requestComplete, isTrue); |
| 210 }); |
| 211 |
| 212 test("completes requests in request order regardless of callback order", |
| 213 () async { |
| 214 var pool = new Pool(2); |
| 215 var resource1 = await pool.request(); |
| 216 var resource2 = await pool.request(); |
| 217 |
| 218 var request1Complete = false; |
| 219 pool.request().then((_) => request1Complete = true); |
| 220 var request2Complete = false; |
| 221 pool.request().then((_) => request2Complete = true); |
| 222 |
| 223 var onRelease1Called = false; |
| 224 var completer1 = new Completer(); |
| 225 resource1.allowRelease(() { |
| 226 onRelease1Called = true; |
| 227 return completer1.future; |
| 228 }); |
| 229 await new Future.delayed(Duration.ZERO); |
| 230 expect(onRelease1Called, isTrue); |
| 231 |
| 232 var onRelease2Called = false; |
| 233 var completer2 = new Completer(); |
| 234 resource2.allowRelease(() { |
| 235 onRelease2Called = true; |
| 236 return completer2.future; |
| 237 }); |
| 238 await new Future.delayed(Duration.ZERO); |
| 239 expect(onRelease2Called, isTrue); |
| 240 expect(request1Complete, isFalse); |
| 241 expect(request2Complete, isFalse); |
| 242 |
| 243 // Complete the second resource's onRelease callback first. Even though it |
| 244 // was triggered by the second blocking request, it should complete the |
| 245 // first one to preserve ordering. |
| 246 completer2.complete(); |
| 247 await new Future.delayed(Duration.ZERO); |
| 248 expect(request1Complete, isTrue); |
| 249 expect(request2Complete, isFalse); |
| 250 |
| 251 completer1.complete(); |
| 252 await new Future.delayed(Duration.ZERO); |
| 253 expect(request1Complete, isTrue); |
| 254 expect(request2Complete, isTrue); |
| 255 }); |
| 256 |
| 257 test("runs onRequest in the zone it was created", () async { |
| 258 var pool = new Pool(1); |
| 259 var resource = await pool.request(); |
| 260 |
| 261 var outerZone = Zone.current; |
| 262 runZoned(() { |
| 263 var innerZone = Zone.current; |
| 264 expect(innerZone, isNot(equals(outerZone))); |
| 265 |
| 266 resource.allowRelease(expectAsync(() { |
| 267 expect(Zone.current, equals(innerZone)); |
| 268 })); |
| 269 }); |
| 270 |
| 271 pool.request(); |
| 272 }); |
| 273 }); |
| 274 |
| 275 group("close()", () { |
| 276 test("disallows request() and withResource()", () { |
| 277 var pool = new Pool(1)..close(); |
| 278 expect(pool.request, throwsStateError); |
| 279 expect(() => pool.withResource(() {}), throwsStateError); |
| 280 }); |
| 281 |
| 282 test("pending requests are fulfilled", () async { |
| 283 var pool = new Pool(1); |
| 284 var resource1 = await pool.request(); |
| 285 expect(pool.request().then((resource2) { |
| 286 resource2.release(); |
| 287 }), completes); |
| 288 expect(pool.close(), completes); |
| 289 resource1.release(); |
| 290 }); |
| 291 |
| 292 test("pending requests are fulfilled with allowRelease", () async { |
| 293 var pool = new Pool(1); |
| 294 var resource1 = await pool.request(); |
| 295 |
| 296 var completer = new Completer(); |
| 297 expect(pool.request().then((resource2) { |
| 298 expect(completer.isCompleted, isTrue); |
| 299 resource2.release(); |
| 300 }), completes); |
| 301 expect(pool.close(), completes); |
| 302 |
| 303 resource1.allowRelease(() => completer.future); |
| 304 await new Future.delayed(Duration.ZERO); |
| 305 |
| 306 completer.complete(); |
| 307 }); |
| 308 |
| 309 test("doesn't complete until all resources are released", () async { |
| 310 var pool = new Pool(2); |
| 311 var resource1 = await pool.request(); |
| 312 var resource2 = await pool.request(); |
| 313 var resource3Future = pool.request(); |
| 314 |
| 315 var resource1Released = false; |
| 316 var resource2Released = false; |
| 317 var resource3Released = false; |
| 318 expect(pool.close().then((_) { |
| 319 expect(resource1Released, isTrue); |
| 320 expect(resource2Released, isTrue); |
| 321 expect(resource3Released, isTrue); |
| 322 }), completes); |
| 323 |
| 324 resource1Released = true; |
| 325 resource1.release(); |
| 326 await new Future.delayed(Duration.ZERO); |
| 327 |
| 328 resource2Released = true; |
| 329 resource2.release(); |
| 330 await new Future.delayed(Duration.ZERO); |
| 331 |
| 332 var resource3 = await resource3Future; |
| 333 resource3Released = true; |
| 334 resource3.release(); |
| 335 }); |
| 336 |
| 337 test("active onReleases complete as usual", () async { |
| 338 var pool = new Pool(1); |
| 339 var resource = await pool.request(); |
| 340 |
| 341 // Set up an onRelease callback whose completion is controlled by |
| 342 // [completer]. |
| 343 var completer = new Completer(); |
| 344 resource.allowRelease(() => completer.future); |
| 345 expect(pool.request().then((_) { |
| 346 expect(completer.isCompleted, isTrue); |
| 347 }), completes); |
| 348 |
| 349 await new Future.delayed(Duration.ZERO); |
| 350 pool.close(); |
| 351 |
| 352 await new Future.delayed(Duration.ZERO); |
| 353 completer.complete(); |
| 354 }); |
| 355 |
| 356 test("inactive onReleases fire", () async { |
| 357 var pool = new Pool(2); |
| 358 var resource1 = await pool.request(); |
| 359 var resource2 = await pool.request(); |
| 360 |
| 361 var completer1 = new Completer(); |
| 362 resource1.allowRelease(() => completer1.future); |
| 363 var completer2 = new Completer(); |
| 364 resource2.allowRelease(() => completer2.future); |
| 365 |
| 366 expect(pool.close().then((_) { |
| 367 expect(completer1.isCompleted, isTrue); |
| 368 expect(completer2.isCompleted, isTrue); |
| 369 }), completes); |
| 370 |
| 371 await new Future.delayed(Duration.ZERO); |
| 372 completer1.complete(); |
| 373 |
| 374 await new Future.delayed(Duration.ZERO); |
| 375 completer2.complete(); |
| 376 }); |
| 377 |
| 378 test("new allowReleases fire immediately", () async { |
| 379 var pool = new Pool(1); |
| 380 var resource = await pool.request(); |
| 381 |
| 382 var completer = new Completer(); |
| 383 expect(pool.close().then((_) { |
| 384 expect(completer.isCompleted, isTrue); |
| 385 }), completes); |
| 386 |
| 387 await new Future.delayed(Duration.ZERO); |
| 388 resource.allowRelease(() => completer.future); |
| 389 |
| 390 await new Future.delayed(Duration.ZERO); |
| 391 completer.complete(); |
| 392 }); |
| 393 |
| 394 test("an onRelease error is piped to the return value", () async { |
| 395 var pool = new Pool(1); |
| 396 var resource = await pool.request(); |
| 397 |
| 398 var completer = new Completer(); |
| 399 resource.allowRelease(() => completer.future); |
| 400 |
| 401 expect(pool.close(), throwsA("oh no!")); |
| 402 |
| 403 await new Future.delayed(Duration.ZERO); |
| 404 completer.completeError("oh no!"); |
| 405 }); |
| 406 }); |
| 407 } |
| 408 |
| 409 /// Returns a function that will cause the test to fail if it's called. |
| 410 /// |
| 411 /// This should only be called within a [FakeAsync.run] zone. |
| 412 Function expectNoAsync() { |
| 413 var stack = new Trace.current(1); |
| 414 return () => registerException( |
| 415 new TestFailure("Expected function not to be called."), stack); |
| 416 } |
| 417 |
| 418 /// A matcher for Futures that asserts that they don't complete. |
| 419 /// |
| 420 /// This should only be called within a [FakeAsync.run] zone. |
| 421 Matcher get doesNotComplete => predicate((future) { |
| 422 expect(future, new isInstanceOf<Future>()); |
| 423 |
| 424 var stack = new Trace.current(1); |
| 425 future.then((_) => registerException( |
| 426 new TestFailure("Expected future not to complete."), stack)); |
| 427 return true; |
| 428 }); |
OLD | NEW |