| // Copyright 2007 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| goog.provide('goog.pubsub.PubSubTest'); |
| goog.setTestOnly('goog.pubsub.PubSubTest'); |
| |
| goog.require('goog.array'); |
| goog.require('goog.pubsub.PubSub'); |
| goog.require('goog.testing.jsunit'); |
| |
| var pubsub; |
| |
| function setUp() { |
| pubsub = new goog.pubsub.PubSub(); |
| } |
| |
| function tearDown() { |
| pubsub.dispose(); |
| } |
| |
| function testConstructor() { |
| assertNotNull('PubSub instance must not be null', pubsub); |
| assertTrue('PubSub instance must have the expected type', |
| pubsub instanceof goog.pubsub.PubSub); |
| } |
| |
| function testDispose() { |
| assertFalse('PubSub instance must not have been disposed of', |
| pubsub.isDisposed()); |
| pubsub.dispose(); |
| assertTrue('PubSub instance must have been disposed of', |
| pubsub.isDisposed()); |
| } |
| |
| function testSubscribeUnsubscribe() { |
| function foo1() { |
| } |
| function bar1() { |
| } |
| function foo2() { |
| } |
| function bar2() { |
| } |
| |
| assertEquals('Topic "foo" must not have any subscribers', 0, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must not have any subscribers', 0, |
| pubsub.getCount('bar')); |
| |
| pubsub.subscribe('foo', foo1); |
| assertEquals('Topic "foo" must have 1 subscriber', 1, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must not have any subscribers', 0, |
| pubsub.getCount('bar')); |
| |
| pubsub.subscribe('bar', bar1); |
| assertEquals('Topic "foo" must have 1 subscriber', 1, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must have 1 subscriber', 1, |
| pubsub.getCount('bar')); |
| |
| pubsub.subscribe('foo', foo2); |
| assertEquals('Topic "foo" must have 2 subscribers', 2, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must have 1 subscriber', 1, |
| pubsub.getCount('bar')); |
| |
| pubsub.subscribe('bar', bar2); |
| assertEquals('Topic "foo" must have 2 subscribers', 2, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must have 2 subscribers', 2, |
| pubsub.getCount('bar')); |
| |
| assertTrue(pubsub.unsubscribe('foo', foo1)); |
| assertEquals('Topic "foo" must have 1 subscriber', 1, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must have 2 subscribers', 2, |
| pubsub.getCount('bar')); |
| |
| assertTrue(pubsub.unsubscribe('foo', foo2)); |
| assertEquals('Topic "foo" must have no subscribers', 0, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must have 2 subscribers', 2, |
| pubsub.getCount('bar')); |
| |
| assertTrue(pubsub.unsubscribe('bar', bar1)); |
| assertEquals('Topic "foo" must have no subscribers', 0, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must have 1 subscriber', 1, |
| pubsub.getCount('bar')); |
| |
| assertTrue(pubsub.unsubscribe('bar', bar2)); |
| assertEquals('Topic "foo" must have no subscribers', 0, |
| pubsub.getCount('foo')); |
| assertEquals('Topic "bar" must have no subscribers', 0, |
| pubsub.getCount('bar')); |
| |
| assertFalse('Unsubscribing a nonexistent topic must return false', |
| pubsub.unsubscribe('baz', foo1)); |
| |
| assertFalse('Unsubscribing a nonexistent function must return false', |
| pubsub.unsubscribe('foo', function() {})); |
| } |
| |
| function testSubscribeUnsubscribeWithContext() { |
| function foo() { |
| } |
| function bar() { |
| } |
| |
| var contextA = {}; |
| var contextB = {}; |
| |
| assertEquals('Topic "X" must not have any subscribers', 0, |
| pubsub.getCount('X')); |
| |
| pubsub.subscribe('X', foo, contextA); |
| assertEquals('Topic "X" must have 1 subscriber', 1, |
| pubsub.getCount('X')); |
| |
| pubsub.subscribe('X', bar); |
| assertEquals('Topic "X" must have 2 subscribers', 2, |
| pubsub.getCount('X')); |
| |
| pubsub.subscribe('X', bar, contextB); |
| assertEquals('Topic "X" must have 3 subscribers', 3, |
| pubsub.getCount('X')); |
| |
| assertFalse('Unknown function/context combination return false', |
| pubsub.unsubscribe('X', foo, contextB)); |
| |
| assertTrue(pubsub.unsubscribe('X', foo, contextA)); |
| assertEquals('Topic "X" must have 2 subscribers', 2, |
| pubsub.getCount('X')); |
| |
| assertTrue(pubsub.unsubscribe('X', bar)); |
| assertEquals('Topic "X" must have 1 subscriber', 1, |
| pubsub.getCount('X')); |
| |
| assertTrue(pubsub.unsubscribe('X', bar, contextB)); |
| assertEquals('Topic "X" must have no subscribers', 0, |
| pubsub.getCount('X')); |
| } |
| |
| function testSubscribeOnce() { |
| var called, context; |
| |
| called = false; |
| pubsub.subscribeOnce('someTopic', function() { |
| called = true; |
| }); |
| assertEquals('Topic must have one subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertFalse('Subscriber must not have been called yet', called); |
| |
| pubsub.publish('someTopic'); |
| assertEquals('Topic must have no subscribers', 0, |
| pubsub.getCount('someTopic')); |
| assertTrue('Subscriber must have been called', called); |
| |
| context = {called: false}; |
| pubsub.subscribeOnce('someTopic', function() { |
| this.called = true; |
| }, context); |
| assertEquals('Topic must have one subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertFalse('Subscriber must not have been called yet', context.called); |
| |
| pubsub.publish('someTopic'); |
| assertEquals('Topic must have no subscribers', 0, |
| pubsub.getCount('someTopic')); |
| assertTrue('Subscriber must have been called', context.called); |
| |
| context = {called: false, value: 0}; |
| pubsub.subscribeOnce('someTopic', function(value) { |
| this.called = true; |
| this.value = value; |
| }, context); |
| assertEquals('Topic must have one subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertFalse('Subscriber must not have been called yet', context.called); |
| assertEquals('Value must have expected value', 0, context.value); |
| |
| pubsub.publish('someTopic', 17); |
| assertEquals('Topic must have no subscribers', 0, |
| pubsub.getCount('someTopic')); |
| assertTrue('Subscriber must have been called', context.called); |
| assertEquals('Value must have been updated', 17, context.value); |
| } |
| |
| function testSubscribeOnce_boundFn() { |
| var context = {called: false, value: 0}; |
| |
| function subscriber(value) { |
| this.called = true; |
| this.value = value; |
| } |
| |
| pubsub.subscribeOnce('someTopic', goog.bind(subscriber, context)); |
| assertEquals('Topic must have one subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertFalse('Subscriber must not have been called yet', context.called); |
| assertEquals('Value must have expected value', 0, context.value); |
| |
| pubsub.publish('someTopic', 17); |
| assertEquals('Topic must have no subscribers', 0, |
| pubsub.getCount('someTopic')); |
| assertTrue('Subscriber must have been called', context.called); |
| assertEquals('Value must have been updated', 17, context.value); |
| } |
| |
| function testSubscribeOnce_partialFn() { |
| var called = false; |
| var value = 0; |
| |
| function subscriber(hasBeenCalled, newValue) { |
| called = hasBeenCalled; |
| value = newValue; |
| } |
| |
| pubsub.subscribeOnce('someTopic', goog.partial(subscriber, true)); |
| assertEquals('Topic must have one subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertFalse('Subscriber must not have been called yet', called); |
| assertEquals('Value must have expected value', 0, value); |
| |
| pubsub.publish('someTopic', 17); |
| assertEquals('Topic must have no subscribers', 0, |
| pubsub.getCount('someTopic')); |
| assertTrue('Subscriber must have been called', called); |
| assertEquals('Value must have been updated', 17, value); |
| } |
| |
| function testSelfResubscribe() { |
| var value = null; |
| |
| function resubscribe(iteration, newValue) { |
| pubsub.subscribeOnce('someTopic', |
| goog.partial(resubscribe, iteration + 1)); |
| value = newValue + ':' + iteration; |
| } |
| |
| pubsub.subscribeOnce('someTopic', goog.partial(resubscribe, 0)); |
| assertEquals('Topic must have 1 subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertNull('Value must be null', value); |
| |
| pubsub.publish('someTopic', 'foo'); |
| assertEquals('Topic must have 1 subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertEquals('Pubsub must not have any pending unsubscribe keys', 0, |
| pubsub.pendingKeys_.length); |
| assertEquals('Value be as expected', 'foo:0', value); |
| |
| pubsub.publish('someTopic', 'bar'); |
| assertEquals('Topic must have 1 subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertEquals('Pubsub must not have any pending unsubscribe keys', 0, |
| pubsub.pendingKeys_.length); |
| assertEquals('Value be as expected', 'bar:1', value); |
| |
| pubsub.publish('someTopic', 'baz'); |
| assertEquals('Topic must have 1 subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertEquals('Pubsub must not have any pending unsubscribe keys', 0, |
| pubsub.pendingKeys_.length); |
| assertEquals('Value be as expected', 'baz:2', value); |
| } |
| |
| function testUnsubscribeByKey() { |
| var key1, key2, key3; |
| |
| key1 = pubsub.subscribe('X', function() {}); |
| key2 = pubsub.subscribe('Y', function() {}); |
| |
| assertEquals('Topic "X" must have 1 subscriber', 1, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have 1 subscriber', 1, |
| pubsub.getCount('Y')); |
| assertNotEquals('Subscription keys must be distinct', key1, key2); |
| |
| pubsub.unsubscribeByKey(key1); |
| assertEquals('Topic "X" must have no subscribers', 0, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have 1 subscriber', 1, |
| pubsub.getCount('Y')); |
| |
| key3 = pubsub.subscribe('X', function() {}); |
| assertEquals('Topic "X" must have 1 subscriber', 1, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have 1 subscriber', 1, |
| pubsub.getCount('Y')); |
| assertNotEquals('Subscription keys must be distinct', key1, key3); |
| assertNotEquals('Subscription keys must be distinct', key2, key3); |
| |
| pubsub.unsubscribeByKey(key1); // Obsolete key; should be no-op. |
| assertEquals('Topic "X" must have 1 subscriber', 1, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have 1 subscriber', 1, |
| pubsub.getCount('Y')); |
| |
| pubsub.unsubscribeByKey(key2); |
| assertEquals('Topic "X" must have 1 subscriber', 1, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have no subscribers', 0, |
| pubsub.getCount('Y')); |
| |
| pubsub.unsubscribeByKey(key3); |
| assertEquals('Topic "X" must have no subscribers', 0, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have no subscribers', 0, |
| pubsub.getCount('Y')); |
| } |
| |
| function testSubscribeUnsubscribeMultiple() { |
| function foo() { |
| } |
| function bar() { |
| } |
| |
| var context = {}; |
| |
| assertEquals('Pubsub channel must not have any subscribers', 0, |
| pubsub.getCount()); |
| |
| assertEquals('Topic "X" must not have any subscribers', 0, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must not have any subscribers', 0, |
| pubsub.getCount('Y')); |
| assertEquals('Topic "Z" must not have any subscribers', 0, |
| pubsub.getCount('Z')); |
| |
| goog.array.forEach(['X', 'Y', 'Z'], function(topic) { |
| pubsub.subscribe(topic, foo); |
| }); |
| assertEquals('Topic "X" must have 1 subscriber', 1, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have 1 subscriber', 1, |
| pubsub.getCount('Y')); |
| assertEquals('Topic "Z" must have 1 subscriber', 1, |
| pubsub.getCount('Z')); |
| |
| goog.array.forEach(['X', 'Y', 'Z'], function(topic) { |
| pubsub.subscribe(topic, bar, context); |
| }); |
| assertEquals('Topic "X" must have 2 subscribers', 2, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have 2 subscribers', 2, |
| pubsub.getCount('Y')); |
| assertEquals('Topic "Z" must have 2 subscribers', 2, |
| pubsub.getCount('Z')); |
| |
| assertEquals('Pubsub channel must have a total of 6 subscribers', 6, |
| pubsub.getCount()); |
| |
| goog.array.forEach(['X', 'Y', 'Z'], function(topic) { |
| pubsub.unsubscribe(topic, foo); |
| }); |
| assertEquals('Topic "X" must have 1 subscriber', 1, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must have 1 subscriber', 1, |
| pubsub.getCount('Y')); |
| assertEquals('Topic "Z" must have 1 subscriber', 1, |
| pubsub.getCount('Z')); |
| |
| goog.array.forEach(['X', 'Y', 'Z'], function(topic) { |
| pubsub.unsubscribe(topic, bar, context); |
| }); |
| assertEquals('Topic "X" must not have any subscribers', 0, |
| pubsub.getCount('X')); |
| assertEquals('Topic "Y" must not have any subscribers', 0, |
| pubsub.getCount('Y')); |
| assertEquals('Topic "Z" must not have any subscribers', 0, |
| pubsub.getCount('Z')); |
| |
| assertEquals('Pubsub channel must not have any subscribers', 0, |
| pubsub.getCount()); |
| } |
| |
| function testPublish() { |
| var context = {}; |
| var fooCalled = false; |
| var barCalled = false; |
| |
| function foo(x, y) { |
| fooCalled = true; |
| assertEquals('x must have expected value', 'x', x); |
| assertEquals('y must have expected value', 'y', y); |
| } |
| |
| function bar(x, y) { |
| barCalled = true; |
| assertEquals('Context must have expected value', context, this); |
| assertEquals('x must have expected value', 'x', x); |
| assertEquals('y must have expected value', 'y', y); |
| } |
| |
| pubsub.subscribe('someTopic', foo); |
| pubsub.subscribe('someTopic', bar, context); |
| |
| assertTrue(pubsub.publish('someTopic', 'x', 'y')); |
| assertTrue('foo() must have been called', fooCalled); |
| assertTrue('bar() must have been called', barCalled); |
| |
| fooCalled = false; |
| barCalled = false; |
| assertTrue(pubsub.unsubscribe('someTopic', foo)); |
| |
| assertTrue(pubsub.publish('someTopic', 'x', 'y')); |
| assertFalse('foo() must not have been called', fooCalled); |
| assertTrue('bar() must have been called', barCalled); |
| |
| fooCalled = false; |
| barCalled = false; |
| pubsub.subscribe('differentTopic', foo); |
| |
| assertTrue(pubsub.publish('someTopic', 'x', 'y')); |
| assertFalse('foo() must not have been called', fooCalled); |
| assertTrue('bar() must have been called', barCalled); |
| } |
| |
| function testPublishEmptyTopic() { |
| var fooCalled = false; |
| function foo() { |
| fooCalled = true; |
| } |
| |
| assertFalse('Publishing to nonexistent topic must return false', |
| pubsub.publish('someTopic')); |
| |
| pubsub.subscribe('someTopic', foo); |
| assertTrue('Publishing to topic with subscriber must return true', |
| pubsub.publish('someTopic')); |
| assertTrue('Foo must have been called', fooCalled); |
| |
| pubsub.unsubscribe('someTopic', foo); |
| fooCalled = false; |
| assertFalse('Publishing to topic without subscribers must return false', |
| pubsub.publish('someTopic')); |
| assertFalse('Foo must nothave been called', fooCalled); |
| } |
| |
| function testSubscribeWhilePublishing() { |
| // It's OK for a subscriber to add a new subscriber to its own topic, |
| // but the newly added subscriber shouldn't be called until the next |
| // publish cycle. |
| |
| var firstCalled = false; |
| var secondCalled = false; |
| |
| pubsub.subscribe('someTopic', function() { |
| pubsub.subscribe('someTopic', function() { |
| secondCalled = true; |
| }); |
| firstCalled = true; |
| }); |
| assertEquals('Topic must have one subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertFalse('No subscriber must have been called yet', |
| firstCalled || secondCalled); |
| |
| pubsub.publish('someTopic'); |
| assertEquals('Topic must have two subscribers', 2, |
| pubsub.getCount('someTopic')); |
| assertTrue('The first subscriber must have been called', |
| firstCalled); |
| assertFalse('The second subscriber must not have been called yet', |
| secondCalled); |
| |
| pubsub.publish('someTopic'); |
| assertEquals('Topic must have three subscribers', 3, |
| pubsub.getCount('someTopic')); |
| assertTrue('The first subscriber must have been called', |
| firstCalled); |
| assertTrue('The second subscriber must also have been called', |
| secondCalled); |
| } |
| |
| function testUnsubscribeWhilePublishing() { |
| // It's OK for a subscriber to unsubscribe another subscriber from its |
| // own topic, but the subscriber in question won't actually be removed |
| // until after publishing is complete. |
| |
| var firstCalled = false; |
| var secondCalled = false; |
| var thirdCalled = false; |
| |
| function first() { |
| assertFalse('unsubscribe() must return false during publishing', |
| pubsub.unsubscribe('X', second)); |
| assertEquals('Topic "X" must still have 3 subscribers', 3, |
| pubsub.getCount('X')); |
| firstCalled = true; |
| } |
| pubsub.subscribe('X', first); |
| |
| function second() { |
| assertEquals('Topic "X" must still have 3 subscribers', 3, |
| pubsub.getCount('X')); |
| secondCalled = true; |
| } |
| pubsub.subscribe('X', second); |
| |
| function third() { |
| assertFalse('unsubscribe() must return false during publishing', |
| pubsub.unsubscribe('X', first)); |
| assertEquals('Topic "X" must still have 3 subscribers', 3, |
| pubsub.getCount('X')); |
| thirdCalled = true; |
| } |
| pubsub.subscribe('X', third); |
| |
| assertEquals('Topic "X" must have 3 subscribers', 3, |
| pubsub.getCount('X')); |
| assertFalse('No subscribers must have been called yet', |
| firstCalled || secondCalled || thirdCalled); |
| |
| assertTrue(pubsub.publish('X')); |
| assertTrue('First function must have been called', firstCalled); |
| assertTrue('Second function must have been called', secondCalled); |
| assertTrue('Third function must have been called', thirdCalled); |
| assertEquals('Topic "X" must have 1 subscriber after publishing', 1, |
| pubsub.getCount('X')); |
| assertEquals('PubSub must not have any subscriptions pending removal', 0, |
| pubsub.pendingKeys_.length); |
| } |
| |
| function testUnsubscribeSelfWhilePublishing() { |
| // It's OK for a subscriber to unsubscribe itself, but it won't actually |
| // be removed until after publishing is complete. |
| |
| var selfDestructCalled = false; |
| |
| function selfDestruct() { |
| assertFalse('unsubscribe() must return false during publishing', |
| pubsub.unsubscribe('someTopic', arguments.callee)); |
| assertEquals('Topic must still have 1 subscriber', 1, |
| pubsub.getCount('someTopic')); |
| selfDestructCalled = true; |
| } |
| |
| pubsub.subscribe('someTopic', selfDestruct); |
| assertEquals('Topic must have 1 subscriber', 1, |
| pubsub.getCount('someTopic')); |
| assertFalse('selfDestruct() must not have been called yet', |
| selfDestructCalled); |
| |
| pubsub.publish('someTopic'); |
| assertTrue('selfDestruct() must have been called', selfDestructCalled); |
| assertEquals('Topic must have no subscribers after publishing', 0, |
| pubsub.getCount('someTopic')); |
| assertEquals('PubSub must not have any subscriptions pending removal', 0, |
| pubsub.pendingKeys_.length); |
| } |
| |
| function testPublishReturnValue() { |
| pubsub.subscribe('X', function() { |
| pubsub.unsubscribe('X', arguments.callee); |
| }); |
| assertTrue('publish() must return true even if the only subscriber ' + |
| 'removes itself during publishing', pubsub.publish('X')); |
| } |
| |
| function testNestedPublish() { |
| var x1 = false; |
| var x2 = false; |
| var y1 = false; |
| var y2 = false; |
| |
| pubsub.subscribe('X', function() { |
| pubsub.publish('Y'); |
| pubsub.unsubscribe('X', arguments.callee); |
| x1 = true; |
| }); |
| |
| pubsub.subscribe('X', function() { |
| x2 = true; |
| }); |
| |
| pubsub.subscribe('Y', function() { |
| pubsub.unsubscribe('Y', arguments.callee); |
| y1 = true; |
| }); |
| |
| pubsub.subscribe('Y', function() { |
| y2 = true; |
| }); |
| |
| pubsub.publish('X'); |
| |
| assertTrue('x1 must be true', x1); |
| assertTrue('x2 must be true', x2); |
| assertTrue('y1 must be true', y1); |
| assertTrue('y2 must be true', y2); |
| } |
| |
| function testClear() { |
| function fn() { |
| } |
| |
| goog.array.forEach(['W', 'X', 'Y', 'Z'], function(topic) { |
| pubsub.subscribe(topic, fn); |
| }); |
| assertEquals('Pubsub channel must have 4 subscribers', 4, |
| pubsub.getCount()); |
| |
| pubsub.clear('W'); |
| assertEquals('Pubsub channel must have 3 subscribers', 3, |
| pubsub.getCount()); |
| |
| goog.array.forEach(['X', 'Y'], function(topic) { |
| pubsub.clear(topic); |
| }); |
| assertEquals('Pubsub channel must have 1 subscriber', 1, |
| pubsub.getCount()); |
| |
| pubsub.clear(); |
| assertEquals('Pubsub channel must have no subscribers', 0, |
| pubsub.getCount()); |
| } |