Repository: aurora Updated Branches: refs/heads/master cb84ed249 -> 86a547b92
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperMapTest.java ---------------------------------------------------------------------- diff --git a/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperMapTest.java b/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperMapTest.java new file mode 100644 index 0000000..1170778 --- /dev/null +++ b/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperMapTest.java @@ -0,0 +1,421 @@ +// ================================================================================================= +// Copyright 2011 Twitter, Inc. +// ------------------------------------------------------------------------------------------------- +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this work except in compliance with the License. +// You may obtain a copy of the License in the LICENSE file, or 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. +// ================================================================================================= + +package com.twitter.common.zookeeper; + +import java.util.AbstractMap.SimpleEntry; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.data.ACL; +import org.junit.Before; +import org.junit.Test; + +import com.twitter.common.base.Command; +import com.twitter.common.collections.Pair; +import com.twitter.common.zookeeper.ZooKeeperMap.Listener; +import com.twitter.common.zookeeper.testing.BaseZooKeeperTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class ZooKeeperMapTest extends BaseZooKeeperTest { + + private static final List<ACL> ACL = ZooDefs.Ids.OPEN_ACL_UNSAFE; + private static final Function<byte[], String> BYTES_TO_STRING = + new Function<byte[], String>() { + @Override + public String apply(byte[] from) { + return new String(from); + }}; + + private static class TestListener implements ZooKeeperMap.Listener<String> { + private final BlockingQueue<Pair<String, String>> queue = + new LinkedBlockingQueue<Pair<String, String>>(); + + public Pair<String, String> waitForUpdate() throws InterruptedException { + return queue.take(); + } + + @Override + public void nodeChanged(String name, String value) { + queue.offer(Pair.of(name, value)); + } + + @Override + public void nodeRemoved(String name) { + queue.offer(Pair.of(name, (String) null)); + } + } + + private ZooKeeperClient zkClient; + private BlockingQueue<Pair<String, String>> entryChanges; + + @Before + public void mySetUp() throws Exception { + zkClient = createZkClient(); + entryChanges = new LinkedBlockingQueue<Pair<String, String>>(); + } + + @Test(expected = KeeperException.NoNodeException.class) + public void testMissingPath() throws Exception { + makeMap("/twitter/doesnt/exist"); + } + + @Test(expected = KeeperException.class) + public void testZooKeeperUnavailableAtConstruction() throws Exception { + final String parentPath = "/twitter/path"; + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + + shutdownNetwork(); // Make zk unavailable. + + makeUninitializedMap(parentPath); + } + + @Test(expected = KeeperException.class) + public void testZooKeeperUnavailableAtInit() throws Exception { + final String parentPath = "/twitter/path"; + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + ZooKeeperMap<String> zkMap = makeUninitializedMap(parentPath); + + shutdownNetwork(); // Make zk unavailable. + + zkMap.init(); + } + + @Test + public void testInitialization() throws Exception { + final String parentPath = "/twitter/path"; + final String node = "node"; + final String nodePath = parentPath + "/" + node; + final String data = "abcdefg"; + + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + ZooKeeperMap<String> zkMap = makeUninitializedMap(parentPath); + + // Map should be empty before initialization + assertTrue(zkMap.isEmpty()); + + zkMap.init(); + + // Now that we've initialized, the data should be synchronously reflected. + assertFalse(zkMap.isEmpty()); + assertEquals(1, zkMap.size()); + assertEquals(data, zkMap.get(node)); + } + + @Test + public void testEmptyStaticMap() throws Exception { + final String parentPath = "/twitter/path"; + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + Map<String, String> zkMap = makeMap(parentPath); + + assertEquals(0, zkMap.size()); + assertTrue(zkMap.isEmpty()); + } + + @Test + public void testStaticMapWithValues() throws Exception { + final String parentPath = "/twitter/path"; + final String node1 = "node1"; + final String node2 = "node2"; + final String nodePath1 = parentPath + "/" + node1; + final String nodePath2 = parentPath + "/" + node2; + final String data1 = "hello World!"; + final String data2 = "evrver232&$"; + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath1, data1.getBytes(), ACL, CreateMode.PERSISTENT); + zkClient.get().create(nodePath2, data2.getBytes(), ACL, CreateMode.PERSISTENT); + + Map<String, String> zkMap = makeMap(parentPath); + + // Test all java.util.Map operations that are implemented. + assertTrue(zkMap.containsKey(node1)); + assertTrue(zkMap.containsKey(node2)); + assertTrue(zkMap.containsValue(data1)); + assertTrue(zkMap.containsValue(data2)); + assertEquals(ImmutableSet.of(new SimpleEntry<String, String>(node1, data1), + new SimpleEntry<String, String>(node2, data2)), zkMap.entrySet()); + assertEquals(data1, zkMap.get(node1)); + assertEquals(data2, zkMap.get(node2)); + assertFalse(zkMap.isEmpty()); + assertEquals(ImmutableSet.of(node1, node2), + zkMap.keySet()); + assertEquals(2, zkMap.size()); + } + + @Test + public void testChangingChildren() throws Exception { + final String parentPath = "/twitter/path"; + final String node1 = "node1"; + final String node2 = "node2"; + final String nodePath1 = parentPath + "/" + node1; + final String nodePath2 = parentPath + "/" + node2; + final String data1 = "wefwe"; + final String data2 = "rtgrtg"; + + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath1, data1.getBytes(), ACL, CreateMode.PERSISTENT); + + Map<String, String> zkMap = makeMap(parentPath); + assertEquals(1, zkMap.size()); + assertEquals(data1, zkMap.get(node1)); + assertEquals(null, zkMap.get(node2)); + + // Make sure the map is updated when a child is added. + zkClient.get().create(nodePath2, data2.getBytes(), ACL, CreateMode.PERSISTENT); + waitForEntryChange(node2, data2); + assertEquals(2, zkMap.size()); + assertEquals(data1, zkMap.get(node1)); + assertEquals(data2, zkMap.get(node2)); + + // Make sure the map is updated when a child is deleted. + zkClient.get().delete(nodePath1, -1); + waitForEntryChange(node1, null); + assertEquals(1, zkMap.size()); + assertEquals(null, zkMap.get(node1)); + assertEquals(data2, zkMap.get(node2)); + } + + @Test + public void testChangingChildValues() throws Exception { + final String parentPath = "/twitter/path"; + final String node = "node"; + final String nodePath = parentPath + "/" + node; + + final String data1 = ""; + final String data2 = "abc"; + final String data3 = "lalala"; + + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath, data1.getBytes(), ACL, CreateMode.PERSISTENT); + + TestListener testListener = new TestListener(); + Map<String, String> zkMap = makeMap(parentPath, testListener); + + assertEquals(Pair.of(node, data1), testListener.waitForUpdate()); + + assertEquals(1, zkMap.size()); + assertEquals(data1, zkMap.get(node)); + + zkClient.get().setData(nodePath, data2.getBytes(), -1); + waitForEntryChange(node, data2); + assertEquals(1, zkMap.size()); + + assertEquals(Pair.of(node, data2), testListener.waitForUpdate()); + + zkClient.get().setData(nodePath, data3.getBytes(), -1); + waitForEntryChange(node, data3); + assertEquals(1, zkMap.size()); + + assertEquals(Pair.of(node, data3), testListener.waitForUpdate()); + } + + @Test + public void testRemoveParentNode() throws Exception { + final String parentPath = "/twitter/path"; + final String node = "node"; + final String nodePath = parentPath + "/" + node; + final String data = "testdata"; + + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + + TestListener testListener = new TestListener(); + Map<String, String> zkMap = makeMap(parentPath, testListener); + assertEquals(1, zkMap.size()); + assertEquals(data, zkMap.get(node)); + + assertEquals(Pair.of(node, data), testListener.waitForUpdate()); + + zkClient.get().delete(nodePath, -1); + zkClient.get().delete(parentPath, -1); + + assertEquals(Pair.of(node, null), testListener.waitForUpdate()); + + waitForEntryChange(node, null); + assertEquals(0, zkMap.size()); + assertTrue(zkMap.isEmpty()); + + // Recreate our node, make sure the map observes it. + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + waitForEntryChange(node, data); + } + + @Test + public void testSessionExpireLogic() throws Exception { + final String parentPath = "/twitter/path"; + final String node1 = "node1"; + final String nodePath1 = parentPath + "/" + node1; + final String data1 = "testdata"; + + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath1, data1.getBytes(), ACL, CreateMode.PERSISTENT); + + Map<String, String> zkMap = makeMap(parentPath); + assertEquals(1, zkMap.size()); + assertEquals(data1, zkMap.get(node1)); + + expireSession(zkClient); + assertEquals(1, zkMap.size()); + assertEquals(data1, zkMap.get(node1)); + + final String node2 = "node2"; + final String nodePath2 = parentPath + "/" + node2; + final String data2 = "testdata2"; + zkClient = createZkClient(); + zkClient.get().create(nodePath2, data2.getBytes(), ACL, CreateMode.PERSISTENT); + + waitForEntryChange(node2, data2); + assertEquals(2, zkMap.size()); + assertEquals(data2, zkMap.get(node2)); + } + + @Test + public void testStaticCreate() throws Exception { + String parentPath = "/twitter/path"; + String node = "node"; + String nodePath = parentPath + "/" + node; + String data = "DaTa"; + + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + + Map<String, String> zkMap = ZooKeeperMap.create(zkClient, parentPath, BYTES_TO_STRING); + assertEquals(1, zkMap.size()); + assertEquals(data, zkMap.get(node)); + } + + private static void checkUnsupported(Command test) { + try { + test.execute(); + fail("Expected UnsupportedOperationException to be thrown."); + } catch (UnsupportedOperationException e) { + // expected + } + } + + @Test + public void testReadOnly() throws Exception { + String parentPath = "/twitter/path"; + final String node = "node"; + String nodePath = parentPath + "/" + node; + String data = "DaTa"; + + ZooKeeperUtils.ensurePath(zkClient, ACL, parentPath); + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + + final Map<String, String> zkMap = ZooKeeperMap.create(zkClient, parentPath, BYTES_TO_STRING); + checkUnsupported(new Command() { + @Override public void execute() { + zkMap.clear(); + } + }); + checkUnsupported(new Command() { + @Override public void execute() { + zkMap.remove(node); + } + }); + checkUnsupported(new Command() { + @Override public void execute() { + zkMap.put("othernode", "othervalue"); + } + }); + checkUnsupported(new Command() { + @Override public void execute() { + zkMap.putAll(ImmutableMap.of("othernode", "othervalue")); + } + }); + checkUnsupported(new Command() { + @Override public void execute() { + zkMap.keySet().iterator().remove(); + } + }); + checkUnsupported(new Command() { + @Override public void execute() { + zkMap.values().iterator().remove(); + } + }); + checkUnsupported(new Command() { + @Override public void execute() { + zkMap.entrySet().iterator().remove(); + } + }); + + // Ensure contents didn't change + assertEquals(1, zkMap.size()); + assertEquals(data, zkMap.get(node)); + } + + private void waitForEntryChange(String key, String value) throws Exception { + Pair<String, String> expectedEntry = Pair.of(key, value); + while (true) { + Pair<String, String> nextEntry = entryChanges.take(); + if (expectedEntry.equals(nextEntry)) { + return; + } + } + } + + private Map<String, String> makeMap(String path) throws Exception { + return makeMap(path, ZooKeeperMap.<String>noopListener()); + } + + private Map<String, String> makeMap(String path, ZooKeeperMap.Listener<String> listener) + throws Exception { + + ZooKeeperMap<String> zkMap = makeUninitializedMap(path, listener); + zkMap.init(); + return zkMap; + } + + private ZooKeeperMap<String> makeUninitializedMap(String path) throws Exception { + return makeUninitializedMap(path, ZooKeeperMap.<String>noopListener()); + } + + private ZooKeeperMap<String> makeUninitializedMap(String path, Listener<String> listener) + throws Exception { + + return new ZooKeeperMap<String>(zkClient, path, BYTES_TO_STRING, listener) { + @Override void putEntry(String key, String value) { + super.putEntry(key, value); + recordEntryChange(key); + } + + @Override void removeEntry(String key) { + super.removeEntry(key); + recordEntryChange(key); + } + + private void recordEntryChange(String key) { + entryChanges.offer(Pair.of(key, get(key))); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperNodeTest.java ---------------------------------------------------------------------- diff --git a/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperNodeTest.java b/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperNodeTest.java new file mode 100644 index 0000000..f1ce3f3 --- /dev/null +++ b/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperNodeTest.java @@ -0,0 +1,284 @@ +package com.twitter.common.zookeeper; + +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.KeeperException.NoNodeException; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import org.easymock.Capture; +import org.junit.Before; +import org.junit.Test; + +import com.twitter.common.base.Closure; +import com.twitter.common.base.Closures; +import com.twitter.common.base.Command; +import com.twitter.common.testing.easymock.EasyMockTest; +import com.twitter.common.zookeeper.ZooKeeperNode.NodeDeserializer; +import com.twitter.common.zookeeper.testing.BaseZooKeeperTest; + +import static org.easymock.EasyMock.aryEq; +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.eq; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.isA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +public class ZooKeeperNodeTest { + public static class LightWeightTests extends EasyMockTest { + private ZooKeeperClient zooKeeperClient; + private ZooKeeper zk; + private NodeDeserializer<String> deserializer; + private Closure<String> dataUpdateListener; + private ZooKeeperNode<String> node; + + @Before + public void setUp() { + zooKeeperClient = createMock(ZooKeeperClient.class); + zk = createMock(ZooKeeper.class); + deserializer = createMock(new Clazz<NodeDeserializer<String>>() { }); + dataUpdateListener = createMock(new Clazz<Closure<String>>() { }); + node = new ZooKeeperNode<String>(zooKeeperClient, "/foo", deserializer, dataUpdateListener); + } + + @Test + public void testWatchersReused() throws Exception { + // 1st init with initial no node exception + expect(zooKeeperClient.registerExpirationHandler(isA(Command.class))).andReturn(null); + expect(zooKeeperClient.get()).andReturn(zk); + Capture<Watcher> dataWatcher1 = createCapture(); + expect(zk.getData(eq("/foo"), capture(dataWatcher1), isA(Stat.class))) + .andThrow(new NoNodeException()); // Force an existence watch to be set + dataUpdateListener.execute(null); + + expect(zooKeeperClient.get()).andReturn(zk); + Capture<Watcher> existenceWatcher1 = createCapture(); + expect(zk.exists(eq("/foo"), capture(existenceWatcher1))).andReturn(new Stat()); + + expect(zooKeeperClient.get()).andReturn(zk); + Capture<Watcher> dataWatcher2 = createCapture(); + expect(zk.getData(eq("/foo"), capture(dataWatcher2), isA(Stat.class))) + .andReturn("bob".getBytes()); + expect(deserializer.deserialize(aryEq("bob".getBytes()), isA(Stat.class))).andReturn("fred"); + dataUpdateListener.execute("fred"); + + // 2nd init with initial no node exception + expect(zooKeeperClient.registerExpirationHandler(isA(Command.class))).andReturn(null); + expect(zooKeeperClient.get()).andReturn(zk); + Capture<Watcher> dataWatcher3 = createCapture(); + expect(zk.getData(eq("/foo"), capture(dataWatcher3), isA(Stat.class))) + .andThrow(new NoNodeException()); // Force an existence watch to be set + dataUpdateListener.execute(null); + + expect(zooKeeperClient.get()).andReturn(zk); + Capture<Watcher> existenceWatcher2 = createCapture(); + expect(zk.exists(eq("/foo"), capture(existenceWatcher2))).andReturn(new Stat()); + + expect(zooKeeperClient.get()).andReturn(zk); + Capture<Watcher> dataWatcher4 = createCapture(); + expect(zk.getData(eq("/foo"), capture(dataWatcher4), isA(Stat.class))) + .andReturn("bip".getBytes()); + expect(deserializer.deserialize(aryEq("bip".getBytes()), isA(Stat.class))).andReturn("frog"); + dataUpdateListener.execute("frog"); + + control.replay(); + + node.init(); + node.init(); + + assertSame(dataWatcher1.getValue(), dataWatcher2.getValue()); + assertSame(dataWatcher2.getValue(), dataWatcher3.getValue()); + assertSame(dataWatcher3.getValue(), dataWatcher4.getValue()); + + assertSame(existenceWatcher1.getValue(), existenceWatcher2.getValue()); + } + } + + public static class HeavyWeightTests extends BaseZooKeeperTest { + + private static final List<ACL> ACL = ZooDefs.Ids.OPEN_ACL_UNSAFE; + + private ZooKeeperClient zkClient; + + private static class Listener<T> implements Closure<T> { + // We use AtomicReference as a wrapper since LinkedBlockingQueue does not allow null values. + private final BlockingQueue<AtomicReference<T>> queue = + new LinkedBlockingQueue<AtomicReference<T>>(); + + public void execute(T item) { + queue.offer(new AtomicReference<T>(item)); + } + + public T waitForUpdate() throws InterruptedException { + return queue.take().get(); + } + } + + private String nodePath; + + @Before + public void mySetUp() throws Exception { + zkClient = createZkClient(); + ZooKeeperUtils.ensurePath(zkClient, ACL, "/twitter"); + nodePath = "/twitter/node"; + } + + @Test + public void testZooKeeperUnavailableAtConstruction() throws Exception { + shutdownNetwork(); // Make zk unavailable. + + // Should be fine. + makeUninitializedNode(nodePath, Closures.<String>noop()); + } + + @Test(expected = KeeperException.class) + public void testZooKeeperUnavailableAtInit() throws Exception { + ZooKeeperNode zkNode = makeUninitializedNode(nodePath, Closures.<String>noop()); + + shutdownNetwork(); // Make zk unavailable. + + zkNode.init(); + } + + @Test + public void testInitialization() throws Exception { + String data = "abcdefg"; + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + ZooKeeperNode zkNode = makeUninitializedNode(nodePath, Closures.<String>noop()); + + // get() should return null before initialization + assertEquals(null, zkNode.get()); + + zkNode.init(); + + // Now that init has been called, the data should be synchronously reflected. + assertEquals(data, zkNode.get()); + } + + @Test + public void testInitialEmptyNode() throws Exception { + Listener<String> listener = new Listener<String>(); + ZooKeeperNode<String> zkNode = makeUninitializedNode(nodePath, listener); + + assertEquals(null, zkNode.get()); + zkNode.init(); + assertEquals(null, listener.waitForUpdate()); + + String data = "abcdefg"; + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + assertEquals(data, listener.waitForUpdate()); + } + + @Test + public void testChangingData() throws Exception { + String data1 = "test_data"; + zkClient.get().create(nodePath, data1.getBytes(), ACL, CreateMode.PERSISTENT); + Listener<String> listener = new Listener<String>(); + TestDeserializer deserializer = new TestDeserializer(); + makeNode(deserializer, nodePath, listener); + + assertEquals(data1, listener.waitForUpdate()); + assertNotNull(deserializer.getStat()); + assertEquals(0, deserializer.getStat().getVersion()); + String data2 = "BLAH"; + zkClient.get().setData(nodePath, data2.getBytes(), -1); + assertEquals(data2, listener.waitForUpdate()); + assertEquals(1, deserializer.getStat().getVersion()); + } + + @Test + public void testRemoveNode() throws Exception { + String data = "testdata"; + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + Listener<String> listener = new Listener<String>(); + TestDeserializer deserializer = new TestDeserializer(); + makeNode(deserializer, nodePath, listener); + + assertEquals(data, listener.waitForUpdate()); + assertNotNull(deserializer.getStat()); + assertEquals(0, deserializer.getStat().getVersion()); + + zkClient.get().delete(nodePath, -1); + assertEquals(null, listener.waitForUpdate()); + assertEquals(0, deserializer.getStat().getVersion()); + + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + assertEquals(data, listener.waitForUpdate()); + assertEquals(0, deserializer.getStat().getVersion()); + } + + @Test + public void testSessionExpireLogic() throws Exception { + String data1 = "testdata"; + zkClient.get().create(nodePath, data1.getBytes(), ACL, CreateMode.PERSISTENT); + Listener<String> listener = new Listener<String>(); + TestDeserializer deserializer = new TestDeserializer(); + makeNode(deserializer, nodePath, listener); + + assertEquals(data1, listener.waitForUpdate()); + assertNotNull(deserializer.getStat()); + assertEquals(0, deserializer.getStat().getVersion()); + + expireSession(zkClient); + assertEquals(data1, listener.waitForUpdate()); + + String data2 = "avewf"; + zkClient = createZkClient(); + zkClient.get().setData(nodePath, data2.getBytes(), -1); + assertEquals(data2, listener.waitForUpdate()); + assertEquals(1, deserializer.getStat().getVersion()); + } + + @Test + public void testStaticCreate() throws Exception { + String data = "stuff"; + zkClient.get().create(nodePath, data.getBytes(), ACL, CreateMode.PERSISTENT); + ZooKeeperNode<String> zkNode = ZooKeeperNode.create(zkClient, nodePath, new TestDeserializer()); + assertEquals(data, zkNode.get()); + } + + private ZooKeeperNode<String> makeNode(TestDeserializer deserializer, String path, + Closure<String> listener) throws Exception { + ZooKeeperNode<String> zkNode = makeUninitializedNode(deserializer, path, listener); + zkNode.init(); + return zkNode; + } + + private ZooKeeperNode<String> makeUninitializedNode(String path, Closure<String> listener) + throws Exception { + return makeUninitializedNode(new TestDeserializer(), path, listener); + } + + private ZooKeeperNode<String> makeUninitializedNode( + ZooKeeperNode.NodeDeserializer<String> deserializer, String path, Closure<String> listener) + throws Exception { + // we test deserializertionWithPair primarily because it is deserializertionally a proper + // superset of deserializertionWithByteArray + return new ZooKeeperNode<String>(zkClient, path, deserializer, listener); + } + + // helper to test Stat population and retrieval + private static final class TestDeserializer implements ZooKeeperNode.NodeDeserializer<String> { + private Stat stat = null; + + @Override + public String deserialize(byte[] data, Stat stat) { + this.stat = stat; + return new String(data); + } + + Stat getStat() { + return stat; + } + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperUtilsTest.java ---------------------------------------------------------------------- diff --git a/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperUtilsTest.java b/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperUtilsTest.java new file mode 100644 index 0000000..1feba27 --- /dev/null +++ b/commons/src/test/java/com/twitter/common/zookeeper/ZooKeeperUtilsTest.java @@ -0,0 +1,141 @@ +// ================================================================================================= +// Copyright 2011 Twitter, Inc. +// ------------------------------------------------------------------------------------------------- +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this work except in compliance with the License. +// You may obtain a copy of the License in the LICENSE file, or 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. +// ================================================================================================= + +package com.twitter.common.zookeeper; + +import com.google.common.base.Charsets; +import com.twitter.common.zookeeper.testing.BaseZooKeeperTest; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException.BadVersionException; +import org.apache.zookeeper.KeeperException.NoAuthException; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.data.Stat; +import org.junit.Test; + +import static com.google.common.testing.junit4.JUnitAsserts.assertNotEqual; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author John Sirois + */ +public class ZooKeeperUtilsTest extends BaseZooKeeperTest { + @Test + public void testEnsurePath() throws Exception { + ZooKeeperClient zkClient = createZkClient(); + zkClient.get().addAuthInfo("digest", "client1:boo".getBytes(Charsets.UTF_8)); + + assertNull(zkClient.get().exists("/foo", false)); + ZooKeeperUtils.ensurePath(zkClient, ZooDefs.Ids.CREATOR_ALL_ACL, "/foo/bar/baz"); + + zkClient = createZkClient(); + zkClient.get().addAuthInfo("digest", "client2:bap".getBytes(Charsets.UTF_8)); + + // Anyone can check for existence in ZK + assertNotNull(zkClient.get().exists("/foo", false)); + assertNotNull(zkClient.get().exists("/foo/bar", false)); + assertNotNull(zkClient.get().exists("/foo/bar/baz", false)); + + try { + zkClient.get().delete("/foo/bar/baz", -1 /* delete no matter what */); + fail("Expected CREATOR_ALL_ACL to be applied to created path and client2 mutations to be " + + "rejected"); + } catch (NoAuthException e) { + // expected + } + } + + @Test + public void testMagicVersionNumberAllowsUnconditionalUpdate() throws Exception { + String nodePath = "/foo"; + ZooKeeperClient zkClient = createZkClient(); + + zkClient.get().create(nodePath, "init".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + + Stat initStat = new Stat(); + byte[] initialData = zkClient.get().getData(nodePath, false, initStat); + assertArrayEquals("init".getBytes(), initialData); + + // bump the version + Stat rev1Stat = zkClient.get().setData(nodePath, "rev1".getBytes(), initStat.getVersion()); + + try { + zkClient.get().setData(nodePath, "rev2".getBytes(), initStat.getVersion()); + fail("expected correct version to be required"); + } catch (BadVersionException e) { + // expected + } + + // expect using the correct version to work + Stat rev2Stat = zkClient.get().setData(nodePath, "rev2".getBytes(), rev1Stat.getVersion()); + assertNotEqual(ZooKeeperUtils.ANY_VERSION, rev2Stat.getVersion()); + + zkClient.get().setData(nodePath, "force-write".getBytes(), ZooKeeperUtils.ANY_VERSION); + Stat forceWriteStat = new Stat(); + byte[] forceWriteData = zkClient.get().getData(nodePath, false, forceWriteStat); + assertArrayEquals("force-write".getBytes(), forceWriteData); + + assertTrue(forceWriteStat.getVersion() > rev2Stat.getVersion()); + assertNotEqual(ZooKeeperUtils.ANY_VERSION, forceWriteStat.getVersion()); + } + + @Test + public void testNormalizingPath() throws Exception { + assertEquals("/", ZooKeeperUtils.normalizePath("/")); + assertEquals("/foo", ZooKeeperUtils.normalizePath("/foo/")); + assertEquals("/foo/bar", ZooKeeperUtils.normalizePath("/foo//bar")); + assertEquals("/foo/bar", ZooKeeperUtils.normalizePath("//foo/bar")); + assertEquals("/foo/bar", ZooKeeperUtils.normalizePath("/foo/bar/")); + assertEquals("/foo/bar", ZooKeeperUtils.normalizePath("/foo/bar//")); + assertEquals("/foo/bar", ZooKeeperUtils.normalizePath("/foo/bar")); + } + + @Test + public void testLenientPaths() { + assertEquals("/", ZooKeeperUtils.normalizePath("///")); + assertEquals("/a/group", ZooKeeperUtils.normalizePath("/a/group")); + assertEquals("/a/group", ZooKeeperUtils.normalizePath("/a/group/")); + assertEquals("/a/group", ZooKeeperUtils.normalizePath("/a//group")); + assertEquals("/a/group", ZooKeeperUtils.normalizePath("/a//group//")); + + try { + ZooKeeperUtils.normalizePath("a/group"); + fail("Relative paths should not be allowed."); + } catch (IllegalArgumentException e) { + // expected + } + + try { + ZooKeeperUtils.normalizePath("/a/./group"); + fail("Relative paths should not be allowed."); + } catch (IllegalArgumentException e) { + // expected + } + + try { + ZooKeeperUtils.normalizePath("/a/../group"); + fail("Relative paths should not be allowed."); + } catch (IllegalArgumentException e) { + // expected + } + } + +} http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/zookeeper/guice/ServerSetModuleTest.java ---------------------------------------------------------------------- diff --git a/commons/src/test/java/com/twitter/common/zookeeper/guice/ServerSetModuleTest.java b/commons/src/test/java/com/twitter/common/zookeeper/guice/ServerSetModuleTest.java new file mode 100644 index 0000000..a9c8575 --- /dev/null +++ b/commons/src/test/java/com/twitter/common/zookeeper/guice/ServerSetModuleTest.java @@ -0,0 +1,63 @@ +package com.twitter.common.zookeeper.guice; + +import java.util.Set; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.util.Providers; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.twitter.common.application.ShutdownRegistry; +import com.twitter.common.application.ShutdownRegistry.ShutdownRegistryImpl; +import com.twitter.common.application.modules.LifecycleModule.ServiceRunner; +import com.twitter.common.application.modules.LocalServiceRegistry; +import com.twitter.common.zookeeper.ServerSet; +import com.twitter.common.zookeeper.ZooKeeperClient; +import com.twitter.common.zookeeper.testing.BaseZooKeeperTest; + +public class ServerSetModuleTest extends BaseZooKeeperTest { + + private IMocksControl control; + + private ServerSet serverSet; + private ShutdownRegistry shutdownRegistry; + private ZooKeeperClient zooKeeperClient; + private LocalServiceRegistry localServiceRegistry; + + @Before + public void mySetUp() { + control = EasyMock.createControl(); + serverSet = control.createMock(ServerSet.class); + + shutdownRegistry = new ShutdownRegistryImpl(); + zooKeeperClient = createZkClient(); + Set<ServiceRunner> localServices = ImmutableSet.of(); + localServiceRegistry = new LocalServiceRegistry(Providers.of(localServices), shutdownRegistry); + } + + @After + public void verify() { + control.verify(); + } + + @Test + public void testInjection() { + control.replay(); + + Guice.createInjector(ImmutableList.of(ServerSetModule.builder().build(), new AbstractModule() { + @Override protected void configure() { + bind(ServerSet.class).toInstance(serverSet); + bind(ZooKeeperClient.class).toInstance(zooKeeperClient); + bind(ShutdownRegistry.class).toInstance(shutdownRegistry); + bind(LocalServiceRegistry.class).toInstance(localServiceRegistry); + } + })); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/resources/com/twitter/common/util/templating/template.st ---------------------------------------------------------------------- diff --git a/commons/src/test/resources/com/twitter/common/util/templating/template.st b/commons/src/test/resources/com/twitter/common/util/templating/template.st new file mode 100644 index 0000000..c4cc8dc --- /dev/null +++ b/commons/src/test/resources/com/twitter/common/util/templating/template.st @@ -0,0 +1,7 @@ +$header$ + +$items:{ item | + The $item.name$ costs \$$item.price$. +}$ + +$footer$ http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/settings.gradle ---------------------------------------------------------------------- diff --git a/settings.gradle b/settings.gradle index 6180227..b097e2f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,4 +12,4 @@ * limitations under the License. */ rootProject.name = 'aurora' -include 'api', 'buildSrc' +include 'api', 'buildSrc', 'commons-args', 'commons'
