package org.gameoss.gridcast.subscriptions; /* * #%L * Gridcast * %% * Copyright (C) 2014 Charles Barry * %% * 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. * #L% */ import org.gameoss.gridcast.p2p.node.NodeId; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; @RunWith(JUnit4.class) public class SubscriptionsTest { // in-place executor private final Executor executor = new Executor() { @Override public void execute(Runnable command) { command.run(); } }; @Test public void basicSubAndUnsub() throws ExecutionException, InterruptedException { NodeId id0 = new NodeId(); NodeId id1 = new NodeId(); NodeId id2 = new NodeId(); // add three nodes to topic "a" Subscriptions subs = new Subscriptions(1000,executor); subs.addSubscription("a", id0); subs.addSubscription("a", id1); subs.addSubscription("a", id2); subs.addFence().get(); // verify subscriptions have been added List<NodeId> ids = subs.getSubscribers("a"); Assert.assertEquals( 3, ids.size() ); Assert.assertTrue(contains(ids, id0) ); Assert.assertTrue(contains(ids, id1)); Assert.assertTrue(contains(ids, id2)); // remove one node from topic "a" subs.removeSubscription("a",id1); subs.addFence().get(); // verify subscription has been removed ids = subs.getSubscribers("a"); Assert.assertEquals(2, ids.size()); Assert.assertTrue(contains(ids, id0)); Assert.assertFalse(contains(ids, id1)); Assert.assertTrue(contains(ids, id2)); subs.shutdown(); } @Test public void subscribeDuplicate() throws ExecutionException, InterruptedException { NodeId id0 = new NodeId(); NodeId id1 = new NodeId(); // add two nodes to topic "a" plus a duplicate node Subscriptions subs = new Subscriptions(1000,executor); subs.addSubscription("a", id0); subs.addSubscription("a", id0); subs.addSubscription("a", id1); subs.addFence().get(); // verify only two subscriptions have been added and the duplicate ignored List<NodeId> ids = subs.getSubscribers("a"); Assert.assertEquals( 2, ids.size() ); Assert.assertTrue(contains(ids, id0) ); Assert.assertTrue(contains(ids, id1)); subs.shutdown(); } @Test public void unsubscribeDuplicate() throws ExecutionException, InterruptedException { NodeId id0 = new NodeId(); NodeId id1 = new NodeId(); // add two nodes to topic "a" Subscriptions subs = new Subscriptions(1000,executor); subs.addSubscription("a", id0); subs.addSubscription("a", id1); subs.addFence().get(); // verify subscriptions have been added List<NodeId> ids = subs.getSubscribers("a"); Assert.assertTrue(contains(ids, id0)); Assert.assertTrue(contains(ids, id1)); // remove same node 3 times subs.removeSubscription("a",id1); subs.removeSubscription("a",id1); subs.removeSubscription("a",id1); subs.addFence().get(); // verify the node was removed and the duplicate calls ignored ids = subs.getSubscribers("a"); Assert.assertEquals(1,ids.size()); Assert.assertTrue(contains(ids, id0)); Assert.assertFalse(contains(ids, id1)); subs.shutdown(); } @Test public void unsubscribeOnEmptyTopic() throws ExecutionException, InterruptedException { NodeId id1 = new NodeId(); Subscriptions subs = new Subscriptions(1000,executor); // remove node that was not subscribed to topic "a" subs.removeSubscription("a",id1); subs.addFence().get(); // verify subscription list is still empty List<NodeId> ids = subs.getSubscribers("a"); Assert.assertNull( ids ); subs.shutdown(); } @Test public void unsubscribeOnlyEntry() throws ExecutionException, InterruptedException { NodeId id = new NodeId(); Subscriptions subs = new Subscriptions(1000,executor); // add and then remove a node to topic "a" subs.addSubscription("a",id); subs.removeSubscription("a",id); subs.addFence().get(); // verify subscription list is empty List<NodeId> ids = subs.getSubscribers("a"); Assert.assertNull( ids ); subs.shutdown(); } @Test public void unsubscribeAll() throws ExecutionException, InterruptedException { NodeId id = new NodeId(); Subscriptions subs = new Subscriptions(1000,executor); // add one node to topics "a", "b", and "c" subs.addSubscription("a", id); subs.addSubscription("b", id); subs.addSubscription("c", id); subs.addFence().get(); // verify node has been added to all three topics Assert.assertTrue( contains(subs.getSubscribers("a"),id) ); Assert.assertTrue( contains(subs.getSubscribers("b"),id) ); Assert.assertTrue( contains(subs.getSubscribers("c"),id) ); // bulk remove the node subs.removeAllSubscriptionsForNode(id); subs.addFence().get(); // verify all three topics have no subscribers Assert.assertFalse( contains(subs.getSubscribers("a"),id) ); Assert.assertFalse( contains(subs.getSubscribers("b"),id) ); Assert.assertFalse( contains(subs.getSubscribers("c"),id) ); subs.shutdown(); } @Test public void listenerAddBefore() throws ExecutionException, InterruptedException { final NodeId id = new NodeId(); Subscriptions subs = new Subscriptions(1000,executor); // setup a listener for topic subscriptions final AtomicInteger subCount = new AtomicInteger(0); final AtomicInteger unsubCount = new AtomicInteger(0); SubscriptionListener listener = subs.addSubscriptionListener("a", new SubscriptionListener() { @Override public void onSubscribe(String topic, NodeId id) { Assert.assertEquals("a", topic); Assert.assertEquals(id, id); subCount.incrementAndGet(); } @Override public void onUnsubscribe(String topic, NodeId id) { Assert.assertEquals("a", topic); Assert.assertEquals(id, id); unsubCount.incrementAndGet(); } }); // add and remove a node to a topic for listener to see. subs.addSubscription("a",id); subs.removeSubscription("a",id); subs.addFence().get(); // verify we got 1 add and 1 remove Assert.assertEquals(1, subCount.get()); Assert.assertEquals(1, unsubCount.get()); subs.removeSubscriptionListener("a",listener); subs.shutdown(); } @Test public void listenerAddAfter() throws ExecutionException, InterruptedException { final NodeId id = new NodeId(); Subscriptions subs = new Subscriptions(1000,executor); // add node to topic subs.addSubscription("a",id); // now add a subscriber listener to make sure it gets called with existing nodes final AtomicInteger subCount = new AtomicInteger(0); final AtomicInteger unsubCount = new AtomicInteger(0); SubscriptionListener listener = subs.addSubscriptionListener("a", new SubscriptionListener() { @Override public void onSubscribe(String topic, NodeId id) { Assert.assertEquals("a", topic); Assert.assertEquals(id, id); subCount.incrementAndGet(); } @Override public void onUnsubscribe(String topic, NodeId id) { Assert.assertEquals("a", topic); Assert.assertEquals(id, id); unsubCount.incrementAndGet(); } }); // remove node from topic subs.removeSubscription("a",id); subs.addFence().get(); // verify we got the add and remove Assert.assertEquals(1, subCount.get()); Assert.assertEquals(1, unsubCount.get()); subs.removeSubscriptionListener("a",listener); subs.shutdown(); } @Test public void basicUserData() throws ExecutionException, InterruptedException { final UserData userData = new UserData("My Test Data"); Subscriptions subs = new Subscriptions(1000,executor); // add user data object to a topic subs.addUserData("a", userData ); subs.addFence().get(); // retrieve user data from a topic List<Object> data = subs.getUserData("a"); Assert.assertEquals(1, data.size()); Assert.assertEquals(userData, data.get(0)); // remove user data from a topic subs.removeUserData("a", userData); subs.addFence().get(); Assert.assertNull(subs.getUserData("a")); subs.shutdown(); } @Test public void globalListenerAndCollect() throws ExecutionException, InterruptedException { final NodeId id = new NodeId(); Subscriptions subs = new Subscriptions(1000,executor); // add a global listener for all topic subs / unsubs final AtomicInteger subCount = new AtomicInteger(0); final AtomicInteger unsubCount = new AtomicInteger(0); SubscriptionListener listener = subs.addGlobalListener( new SubscriptionListener() { private int state = 0; @Override public void onSubscribe(String topic, NodeId id) { unsubCount.addAndGet(1); switch (state) { case 0: Assert.assertEquals("a", topic); state = 1; break; case 1: Assert.assertEquals("b", topic); state = 2; break; case 2: Assert.assertEquals("c", topic); state = 3; break; default: Assert.fail("Invalid state"); break; } } @Override public void onUnsubscribe(String topic, NodeId id) { subCount.addAndGet(1); switch (state) { case 3: Assert.assertEquals("a", topic); state = 4; break; case 4: Assert.assertEquals("b", topic); state = 5; break; case 5: Assert.assertEquals("c", topic); state = 6; break; default: Assert.fail("Invalid state"); break; } } }); // add node to topic "a", "b", "c" subs.addSubscription("a", id); subs.addSubscription("b", id); subs.addSubscription("c", id); subs.addFence().get(); // verify node was added Assert.assertTrue( contains(subs.getSubscribers("a"),id) ); Assert.assertTrue( contains(subs.getSubscribers("b"),id) ); Assert.assertTrue( contains(subs.getSubscribers("c"),id) ); // queue query for list of topics a node is subscribed subs.collectSubscriptionsForNode(id, new CollectListener() { @Override public void onCollectDone(NodeId nodeId, List<String> topics) { Assert.assertEquals( nodeId, id); Assert.assertTrue( topics.contains("a") ); Assert.assertTrue( topics.contains("b") ); Assert.assertTrue( topics.contains("c") ); } }); subs.addFence().get(); // remove node from topics subs.removeSubscription("a", id); subs.removeSubscription("b", id); subs.removeSubscription("c", id); subs.addFence().get(); // verify unsubscribe Assert.assertFalse( contains(subs.getSubscribers("a"),id) ); Assert.assertFalse( contains(subs.getSubscribers("b"),id) ); Assert.assertFalse( contains(subs.getSubscribers("c"),id) ); // verify global listener was called for each sub and unsub Assert.assertEquals(3, subCount.get()); Assert.assertEquals(3, unsubCount.get()); subs.removeGlobalListener(listener); subs.shutdown(); } /** * Array.contains with added null check. * * @param array * @param searchValue * @return true if the array contains the value, otherwise false. */ private boolean contains(List<NodeId> array, NodeId searchValue) { return array != null && array.contains(searchValue); } /** * Test UserData object */ private static class UserData { private String data; private UserData(String data) { this.data = data; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserData userData = (UserData) o; return data.equals(userData.data); } @Override public int hashCode() { return data.hashCode(); } } }