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 library gcloud.test.service_scope_test; |
| 6 |
| 7 import 'dart:async'; |
| 8 |
| 9 import 'package:gcloud/service_scope.dart' as ss; |
| 10 import 'package:unittest/unittest.dart'; |
| 11 |
| 12 main() { |
| 13 test('no-service-scope', () { |
| 14 expect(() => ss.register(1, 'foobar'), throwsA(isStateError)); |
| 15 expect(() => ss.registerScopeExitCallback(() {}), throwsA(isStateError)); |
| 16 expect(() => ss.lookup(1), throwsA(isStateError)); |
| 17 |
| 18 var c = new Completer.sync(); |
| 19 ss.fork(expectAsync(() { |
| 20 c.complete(); |
| 21 return new Future.value(); |
| 22 })); |
| 23 |
| 24 // Assert that after fork()ing we still don't have a service scope outside |
| 25 // of the zone created by the fork()ing. |
| 26 c.future.then(expectAsync((_) { |
| 27 expect(() => ss.register(1, 'foobar'), throwsA(isStateError)); |
| 28 expect(() => ss.registerScopeExitCallback(() {}), throwsA(isStateError)); |
| 29 expect(() => ss.lookup(1), throwsA(isStateError)); |
| 30 })); |
| 31 }); |
| 32 |
| 33 test('non-existent-key', () { |
| 34 return ss.fork(expectAsync(() { |
| 35 expect(ss.lookup(1), isNull); |
| 36 return new Future.value(); |
| 37 })); |
| 38 }); |
| 39 |
| 40 test('fork-callback-returns-non-future', () { |
| 41 // The closure passed to fork() must return a future. |
| 42 expect(() => ss.fork(expectAsync(() => null)), |
| 43 throwsA(isArgumentError)); |
| 44 }); |
| 45 |
| 46 test('error-on-double-insert', () { |
| 47 // Ensure that inserting twice with the same key results in an error. |
| 48 return ss.fork(expectAsync(() => new Future.sync(() { |
| 49 ss.register(1, 'firstValue'); |
| 50 expect(() => ss.register(1, 'firstValue'), throwsA(isArgumentError)); |
| 51 }))); |
| 52 }); |
| 53 |
| 54 test('only-cleanup', () { |
| 55 return ss.fork(expectAsync(() => new Future.sync(() { |
| 56 ss.registerScopeExitCallback(expectAsync(() {})); |
| 57 }))); |
| 58 }); |
| 59 |
| 60 test('correct-insertion-and-cleanup-order', () { |
| 61 // Ensure cleanup functions are called in the reverse order of inserting |
| 62 // their entries. |
| 63 int insertions = 0; |
| 64 return ss.fork(expectAsync(() => new Future.value(() { |
| 65 int NUM = 10; |
| 66 |
| 67 for (int i = 0; i < NUM; i++) { |
| 68 var key = i; |
| 69 |
| 70 insertions++; |
| 71 ss.register(key, 'value$i'); |
| 72 ss.registerScopeExitCallback(expectAsync(() { |
| 73 expect(insertions, equals(i + 1)); |
| 74 insertions--; |
| 75 })); |
| 76 |
| 77 for (int j = 0; j <= NUM; j++) { |
| 78 if (j <= i) { |
| 79 expect(ss.lookup(key), 'value$i'); |
| 80 } else { |
| 81 expect(ss.lookup(key), isNull); |
| 82 } |
| 83 } |
| 84 } |
| 85 }))); |
| 86 }); |
| 87 |
| 88 test('onion-cleanup', () { |
| 89 // Ensures that a cleanup method can look up things registered before it. |
| 90 return ss.fork(expectAsync(() { |
| 91 ss.registerScopeExitCallback(expectAsync(() { |
| 92 expect(ss.lookup(1), isNull); |
| 93 expect(ss.lookup(2), isNull); |
| 94 })); |
| 95 ss.register(1, 'value1'); |
| 96 ss.registerScopeExitCallback(expectAsync(() { |
| 97 expect(ss.lookup(1), equals('value1')); |
| 98 expect(ss.lookup(2), isNull); |
| 99 })); |
| 100 ss.register(2, 'value2', onScopeExit: expectAsync(() { |
| 101 expect(ss.lookup(1), equals('value1')); |
| 102 expect(ss.lookup(2), isNull); |
| 103 })); |
| 104 ss.registerScopeExitCallback(expectAsync(() { |
| 105 expect(ss.lookup(1), 'value1'); |
| 106 expect(ss.lookup(2), 'value2'); |
| 107 })); |
| 108 return new Future.value(); |
| 109 })); |
| 110 }); |
| 111 |
| 112 test('correct-insertion-and-cleanup-order--errors', () { |
| 113 // Ensure that all cleanup functions will be called - even if some of them |
| 114 // result in an error. |
| 115 // Ensure the fork() error message contains all error messages from the |
| 116 // failed cleanup() calls. |
| 117 int insertions = 0; |
| 118 return ss.fork(() => new Future.sync(() { |
| 119 for (int i = 0; i < 10; i++) { |
| 120 insertions++; |
| 121 ss.register(i, 'value$i'); |
| 122 ss.registerScopeExitCallback(() { |
| 123 expect(insertions, equals(i + 1)); |
| 124 insertions--; |
| 125 if (i.isEven) throw 'xx${i}yy'; |
| 126 }); |
| 127 } |
| 128 })).catchError(expectAsync((e, _) { |
| 129 for (int i = 0; i < 10; i++) { |
| 130 expect('$e'.contains('xx${i}yy'), equals(i.isEven)); |
| 131 } |
| 132 })); |
| 133 }); |
| 134 |
| 135 test('service-scope-destroyed-after-callback-completes', () { |
| 136 // Ensure that once the closure passed to fork() completes, the service |
| 137 // scope is destroyed. |
| 138 return ss.fork(expectAsync(() => new Future.sync(() { |
| 139 var key = 1; |
| 140 ss.register(key, 'firstValue'); |
| 141 ss.registerScopeExitCallback(Zone.current.bindCallback(() { |
| 142 // Spawn an async task which will be run after the cleanups to ensure |
| 143 // the service scope got destroyed. |
| 144 Timer.run(expectAsync(() { |
| 145 expect(() => ss.lookup(key), throwsA(isStateError)); |
| 146 expect(() => ss.register(2, 'value'), throwsA(isStateError)); |
| 147 expect(() => ss.registerScopeExitCallback(() {}), |
| 148 throwsA(isStateError)); |
| 149 })); |
| 150 })); |
| 151 expect(ss.lookup(key), equals('firstValue')); |
| 152 }))); |
| 153 }); |
| 154 |
| 155 test('override-parent-value', () { |
| 156 // Ensure that once the closure passed to fork() completes, the service |
| 157 // scope is destroyed. |
| 158 return ss.fork(expectAsync(() => new Future.sync(() { |
| 159 var key = 1; |
| 160 ss.register(key, 'firstValue'); |
| 161 expect(ss.lookup(key), equals('firstValue')); |
| 162 |
| 163 return ss.fork(expectAsync(() => new Future.sync(() { |
| 164 ss.register(key, 'secondValue'); |
| 165 expect(ss.lookup(key), equals('secondValue')); |
| 166 }))); |
| 167 }))); |
| 168 }); |
| 169 |
| 170 test('fork-onError-handler', () { |
| 171 // Ensure that once the closure passed to fork() completes, the service |
| 172 // scope is destroyed. |
| 173 ss.fork(expectAsync(() { |
| 174 Timer.run(() => throw new StateError('foobar')); |
| 175 return new Future.value(); |
| 176 }), onError: expectAsync((error, _) { |
| 177 expect(error, isStateError); |
| 178 })); |
| 179 }); |
| 180 |
| 181 test('nested-fork-and-insert', () { |
| 182 // Ensure that independently fork()ed serice scopes can insert keys |
| 183 // independently and they cannot see each others values but can see parent |
| 184 // service scope values. |
| 185 var rootKey = 1; |
| 186 var subKey = 2; |
| 187 var subKey1 = 3; |
| 188 var subKey2 = 4; |
| 189 |
| 190 return ss.fork(expectAsync(() { |
| 191 int cleanupFork1 = 0; |
| 192 int cleanupFork2 = 0; |
| 193 |
| 194 ss.register(rootKey, 'root'); |
| 195 ss.registerScopeExitCallback(expectAsync(() { |
| 196 expect(cleanupFork1, equals(2)); |
| 197 expect(cleanupFork2, equals(2)); |
| 198 })); |
| 199 expect(ss.lookup(rootKey), equals('root')); |
| 200 |
| 201 Future spawnChild(ownSubKey, otherSubKey, int i, cleanup) { |
| 202 return ss.fork(expectAsync(() => new Future.sync(() { |
| 203 ss.register(subKey, 'fork$i'); |
| 204 ss.registerScopeExitCallback(cleanup); |
| 205 ss.register(ownSubKey, 'sub$i'); |
| 206 ss.registerScopeExitCallback(cleanup); |
| 207 |
| 208 expect(ss.lookup(rootKey), equals('root')); |
| 209 expect(ss.lookup(subKey), equals('fork$i')); |
| 210 expect(ss.lookup(ownSubKey), equals('sub$i')); |
| 211 expect(ss.lookup(otherSubKey), isNull); |
| 212 }))); |
| 213 } |
| 214 |
| 215 return Future.wait([ |
| 216 spawnChild(subKey1, subKey2, 1, () => cleanupFork1++), |
| 217 spawnChild(subKey2, subKey1, 2, () => cleanupFork2++), |
| 218 ]); |
| 219 })); |
| 220 }); |
| 221 } |
OLD | NEW |