| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library isolate.test.registry_test; |
| |
| import "dart:async"; |
| import "dart:isolate"; |
| |
| import "package:isolate/isolate_runner.dart"; |
| import "package:isolate/registry.dart"; |
| |
| import "package:test/test.dart"; |
| |
| const MS = const Duration(milliseconds: 1); |
| |
| void main() { |
| group("lookup", testLookup); |
| group("AddLookup", testAddLookup); |
| group("AddRemoveTags", testAddRemoveTags); |
| group("Remove", testRemove); |
| group("CrossIsolate", testCrossIsolate); |
| group("Timeout", testTimeout); |
| group("MultiRegistry", testMultiRegistry); |
| group("ObjectsAndTags", testObjectsAndTags); |
| } |
| |
| class Oddity { |
| static const int EVEN = 0; |
| static const int ODD = 1; |
| } |
| |
| Future<List> waitAll(int n, Future action(int n)) { |
| return Future.wait(new Iterable.generate(n, action)); |
| } |
| |
| void testLookup() { |
| test("All", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| return waitAll(10, (i) { |
| var element = new Element(i); |
| var tag = i.isEven ? Oddity.EVEN : Oddity.ODD; |
| return registry.add(element, tags: [tag]); |
| }).then((_) { |
| return registry.lookup(); |
| }).then((all) { |
| expect(all.length, 10); |
| expect(all.map((v) => v.id).toList()..sort(), |
| [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Odd", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| return waitAll(10, (i) { |
| var element = new Element(i); |
| var tag = i.isEven ? Oddity.EVEN : Oddity.ODD; |
| return registry.add(element, tags: [tag]); |
| }).then((_) { |
| return registry.lookup(tags: [Oddity.ODD]); |
| }).then((all) { |
| expect(all.length, 5); |
| expect(all.map((v) => v.id).toList()..sort(), [1, 3, 5, 7, 9]); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Max", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| return waitAll(10, (i) { |
| var element = new Element(i); |
| var tag = i.isEven ? Oddity.EVEN : Oddity.ODD; |
| return registry.add(element, tags: [tag]); |
| }).then((_) { |
| return registry.lookup(max: 5); |
| }).then((all) { |
| expect(all.length, 5); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("MultiTag", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| return waitAll(25, (i) { |
| var element = new Element(i); |
| // Collect all numbers dividing i. |
| var tags = [i]; |
| for (int j = 2; j < 25; j++) { |
| if (i % j == 0) tags.add(j); |
| } |
| return registry.add(element, tags: tags); |
| }).then((_) { |
| return registry.lookup(tags: [2, 3]); |
| }).then((all) { |
| expect(all.length, 5); |
| expect(all.map((v) => v.id).toList()..sort(), [0, 6, 12, 18, 24]); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("MultiTagMax", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| return waitAll(25, (i) { |
| var element = new Element(i); |
| // Collect all numbers dividing i. |
| var tags = [i]; |
| for (int j = 2; j < 25; j++) { |
| if (i % j == 0) tags.add(j); |
| } |
| return registry.add(element, tags: tags); |
| }).then((_) { |
| return registry.lookup(tags: [2, 3], max: 3); |
| }).then((all) { |
| expect(all.length, 3); |
| expect(all.every((v) => (v.id % 6) == 0), isTrue); |
| }).whenComplete(regman.close); |
| }); |
| } |
| |
| void testAddLookup() { |
| test("Add-lookup-identical", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object).then((_) { |
| return registry.lookup(); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Add-multiple-identical", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object1 = new Object(); |
| var object2 = new Object(); |
| var object3 = new Object(); |
| var objects = [object1, object2, object3]; |
| return Future.wait(objects.map(registry.add)).then((_) { |
| return registry.lookup(); |
| }).then((entries) { |
| expect(entries, hasLength(3)); |
| for (var entry in entries) { |
| expect(entry, isIn(objects)); |
| } |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Add-twice", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object).then((_) { |
| return registry.add(object); |
| }).then((_) { |
| fail("Unreachable"); |
| }, onError: (e, s) { |
| expect(e, isStateError); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Add-lookup-add-lookup", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| var object2 = new Object(); |
| return registry.add(object).then((_) { |
| return registry.lookup(); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| return registry.add(object2); |
| }).then((_) { |
| return registry.lookup(); |
| }).then((entries) { |
| expect(entries, hasLength(2)); |
| var entry1 = entries.first; |
| var entry2 = entries.last; |
| if (object == entry1) { |
| expect(entry2, same(object2)); |
| } else { |
| expect(entry1, same(object)); |
| } |
| }).whenComplete(regman.close); |
| }); |
| |
| test("lookup-add-lookup", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.lookup().then((entries) { |
| expect(entries, isEmpty); |
| return registry.add(object); |
| }).then((_) { |
| return registry.lookup(); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Add-multiple-tags", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object1 = new Object(); |
| var object2 = new Object(); |
| var object3 = new Object(); |
| return registry.add(object1, tags: [1, 3, 5, 7]).then((_) { |
| return registry.add(object2, tags: [2, 3, 6, 7]); |
| }).then((_) { |
| return registry.add(object3, tags: [4, 5, 6, 7]); |
| }).then((_) { |
| return registry.lookup(tags: [3]); |
| }).then((entries) { |
| expect(entries, hasLength(2)); |
| expect(entries.first == object1 || entries.last == object1, isTrue); |
| expect(entries.first == object2 || entries.last == object2, isTrue); |
| }).then((_) { |
| return registry.lookup(tags: [2]); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object2)); |
| }).then((_) { |
| return registry.lookup(tags: [3, 6]); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object2)); |
| }).whenComplete(regman.close); |
| }); |
| } |
| |
| void testRemove() { |
| test("Add-remove", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object).then((removeCapability) { |
| return registry.lookup().then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| return registry.remove(object, removeCapability); |
| }); |
| }).then((removeSuccess) { |
| expect(removeSuccess, isTrue); |
| return registry.lookup(); |
| }).then((entries) { |
| expect(entries, isEmpty); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Add-remove-fail", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object).then((removeCapability) { |
| return registry.lookup().then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| return registry.remove(object, new Capability()); |
| }); |
| }).then((removeSuccess) { |
| expect(removeSuccess, isFalse); |
| }).whenComplete(regman.close); |
| }); |
| } |
| |
| void testAddRemoveTags() { |
| test("Add-remove-tag", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object).then((removeCapability) { |
| return registry.lookup(tags: ["x"]); |
| }).then((entries) { |
| expect(entries, isEmpty); |
| return registry.addTags([object], ["x"]); |
| }).then((_) { |
| return registry.lookup(tags: ["x"]); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| return registry.removeTags([object], ["x"]); |
| }).then((_) { |
| return registry.lookup(tags: ["x"]); |
| }).then((entries) { |
| expect(entries, isEmpty); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Tag-twice", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object, tags: ["x"]).then((removeCapability) { |
| return registry.lookup(tags: ["x"]); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| // Adding the same tag twice is allowed, but does nothing. |
| return registry.addTags([object], ["x"]); |
| }).then((_) { |
| return registry.lookup(tags: ["x"]); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| // Removing the tag once is enough to remove it. |
| return registry.removeTags([object], ["x"]); |
| }).then((_) { |
| return registry.lookup(tags: ["x"]); |
| }).then((entries) { |
| expect(entries, isEmpty); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Add-remove-multiple", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| var object1 = new Object(); |
| var object2 = new Object(); |
| var object3 = new Object(); |
| var objects = [object1, object2, object3]; |
| return Future.wait(objects.map(registry.add)).then((_) { |
| return registry.addTags([object1, object2], ["x", "y"]); |
| }).then((_) { |
| return registry.addTags([object1, object3], ["z", "w"]); |
| }).then((_) { |
| return registry.lookup(tags: ["x", "z"]); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object1)); |
| return registry.removeTags([object1, object2], ["x", "z"]); |
| }).then((_) { |
| return registry.lookup(tags: ["z"]); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object3)); |
| }).whenComplete(regman.close); |
| }); |
| |
| test("Remove-wrong-object", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry = regman.registry; |
| expect(() => registry.removeTags([new Object()], ["x"]), throwsStateError); |
| regman.close(); |
| }); |
| } |
| |
| var _regmen = {}; |
| Registry createRegMan(id) { |
| var regman = new RegistryManager(); |
| _regmen[id] = regman; |
| return regman.registry; |
| } |
| |
| void closeRegMan(id) { |
| _regmen.remove(id).close(); |
| } |
| |
| void testCrossIsolate() { |
| var object = new Object(); |
| test("regman-other-isolate", () { |
| // Add, lookup and remove object in other isolate. |
| return IsolateRunner.spawn().then((isolate) { |
| isolate.run(createRegMan, 1).then((registry) { |
| return registry.add(object, tags: ["a", "b"]).then((removeCapability) { |
| return registry.lookup(tags: ["a"]).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, same(object)); |
| return registry.remove(entries.first, removeCapability); |
| }).then((removeSuccess) { |
| expect(removeSuccess, isTrue); |
| }); |
| }); |
| }).whenComplete(() { |
| return isolate.run(closeRegMan, 1); |
| }).whenComplete(() { |
| return isolate.close(); |
| }); |
| }); |
| }); |
| } |
| |
| void testTimeout() { |
| test("Timeout-add", () { |
| RegistryManager regman = new RegistryManager(timeout: MS * 500); |
| Registry registry = regman.registry; |
| regman.close(); |
| return registry.add(new Object()).then((_) { |
| fail("unreachable"); |
| }, onError: (e, s) { |
| expect(e is TimeoutException, isTrue); |
| }); |
| }); |
| |
| test("Timeout-remove", () { |
| RegistryManager regman = new RegistryManager(timeout: MS * 500); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object).then((rc) { |
| regman.close(); |
| return registry.remove(object, rc).then((_) { |
| fail("unreachable"); |
| }, onError: (e, s) { |
| expect(e is TimeoutException, isTrue); |
| }); |
| }); |
| }); |
| |
| test("Timeout-addTags", () { |
| RegistryManager regman = new RegistryManager(timeout: MS * 500); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object).then((rc) { |
| regman.close(); |
| return registry.addTags([object], ["x"]).then((_) { |
| fail("unreachable"); |
| }, onError: (e, s) { |
| expect(e is TimeoutException, isTrue); |
| }); |
| }); |
| }); |
| |
| test("Timeout-removeTags", () { |
| RegistryManager regman = new RegistryManager(timeout: MS * 500); |
| Registry registry = regman.registry; |
| var object = new Object(); |
| return registry.add(object).then((rc) { |
| regman.close(); |
| return registry.removeTags([object], ["x"]).then((_) { |
| fail("unreachable"); |
| }, onError: (e, s) { |
| expect(e is TimeoutException, isTrue); |
| }); |
| }); |
| }); |
| |
| test("Timeout-lookup", () { |
| RegistryManager regman = new RegistryManager(timeout: MS * 500); |
| Registry registry = regman.registry; |
| regman.close(); |
| registry.lookup().then((_) { |
| fail("unreachable"); |
| }, onError: (e, s) { |
| expect(e is TimeoutException, isTrue); |
| }); |
| }); |
| } |
| |
| void testMultiRegistry() { |
| test("dual-registry", () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry1 = regman.registry; |
| Registry registry2 = regman.registry; |
| var l1 = ["x"]; |
| var l2; |
| return registry1.add(l1, tags: ["y"]).then((removeCapability) { |
| return registry2.lookup().then((entries) { |
| expect(entries, hasLength(1)); |
| l2 = entries.first; |
| expect(l2, equals(l1)); |
| // The object for registry2 is not identical the one for registry1. |
| expect(!identical(l1, l2), isTrue); |
| // Removing the registry1 object through registry2 doesn't work. |
| return registry2.remove(l1, removeCapability); |
| }).then((removeSuccess) { |
| expect(removeSuccess, isFalse); |
| return registry2.remove(l2, removeCapability); |
| }).then((removeSuccess) { |
| expect(removeSuccess, isTrue); |
| return registry1.lookup(); |
| }).then((entries) { |
| expect(entries, isEmpty); |
| }); |
| }).whenComplete(regman.close); |
| }); |
| } |
| |
| void testObjectsAndTags() { |
| testObject(object) { |
| String name = "Transfer-${object.runtimeType}"; |
| test(name, () { |
| RegistryManager regman = new RegistryManager(); |
| Registry registry1 = regman.registry; |
| Registry registry2 = regman.registry; |
| return registry1.add(object, tags: [object]).then((removeCapability) { |
| return registry2.lookup().then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, equals(object)); |
| return registry2.lookup(tags: [object]); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, equals(object)); |
| return registry2.removeTags([entries.first], [object]); |
| }).then((_) { |
| return registry2.lookup(); |
| }).then((entries) { |
| expect(entries, hasLength(1)); |
| expect(entries.first, equals(object)); |
| return registry2.remove(entries.first, removeCapability); |
| }).then((removeSuccess) { |
| expect(removeSuccess, isTrue); |
| return registry2.lookup(); |
| }).then((entries) { |
| expect(entries, isEmpty); |
| }); |
| }).whenComplete(regman.close); |
| }); |
| } |
| |
| // Test objects that are sendable between equivalent isolates and |
| // that has an operator== that works after cloning (for use as tags). |
| testObject(42); |
| testObject(3.14); |
| testObject("string"); |
| testObject(true); |
| testObject(null); |
| testObject(new Element(42)); |
| testObject(#symbol); |
| testObject(#_privateSymbol); |
| testObject(new Capability()); |
| testObject(topLevelFunction); |
| } |
| |
| class Element { |
| final int id; |
| Element(this.id); |
| int get hashCode => id; |
| bool operator ==(Object other) => other is Element && id == other.id; |
| } |
| |
| void topLevelFunction() {} |