You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by br...@apache.org on 2008/09/05 22:20:21 UTC

svn commit: r692527 - in /hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test: AsyncOps.java AsyncOpsTest.java

Author: breed
Date: Fri Sep  5 13:20:20 2008
New Revision: 692527

URL: http://svn.apache.org/viewvc?rev=692527&view=rev
Log:
ZOOKEEPER-128 test coverage on async client operations needs to be improved

Added:
    hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOps.java
    hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java

Added: hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOps.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOps.java?rev=692527&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOps.java (added)
+++ hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOps.java Fri Sep  5 13:20:20 2008
@@ -0,0 +1,487 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.zookeeper.test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.AsyncCallback.ACLCallback;
+import org.apache.zookeeper.AsyncCallback.ChildrenCallback;
+import org.apache.zookeeper.AsyncCallback.DataCallback;
+import org.apache.zookeeper.AsyncCallback.StatCallback;
+import org.apache.zookeeper.AsyncCallback.StringCallback;
+import org.apache.zookeeper.AsyncCallback.VoidCallback;
+import org.apache.zookeeper.KeeperException.Code;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+/**
+ * The intent of these classes is to support testing the functionality of
+ * asynchronous client operations. Both positive as well as negative tests.
+ * 
+ * This code acts as a "contract checker" of sorts. We look at the 
+ * actual output as well as the expected output - if the actual output 
+ * changes over time this code should catch the regression and alert to a 
+ * potentially unwanted (unexpected) change.
+ * 
+ * In addition these classes can be re-used by other tests that need to
+ * perform these operations. In general the classes err on the side of
+ * convention over a lot of setup - such that you can use start using them
+ * w/o a lot of though (default path/data/acl/etc...). See AsyncOpsTest
+ * for some good examples of use.
+ */
+public class AsyncOps {
+    /**
+     * This is the base class for all of the async callback classes. It will
+     * verify the expected value against the actual value.
+     * 
+     * Basic operation is that the subclasses will generate an "expected" value
+     * which is defined by the "toString" method of the subclass. This is
+     * passed through to the verify clause by specifying it as the ctx object
+     * of each async call (processResult methods get the ctx as part of
+     * the callback). Additionally the callback will also overwrite any
+     * instance fields with matching parameter arguments to the processResult
+     * method. The cb instance can then compare the expected to the
+     * actual value by again calling toString and comparing the two.
+     * 
+     * The format of each expected value differs (is defined) by subclass.
+     * Generally the expected value starts with the result code (rc) and path
+     * of the node being operated on, followed by the fields specific to
+     * each operation type (cb subclass). For example ChildrenCB specifies
+     * a list of the expected children suffixed onto the rc and path. See
+     * the toString() method of each subclass for details of it's format. 
+     */
+    public static abstract class AsyncCB {
+        protected final ZooKeeper zk;
+        protected long defaultTimeoutMillis = 30000;
+        
+        /** the latch is used to await the results from the server */
+        CountDownLatch latch;
+
+        int rc = 0;
+        String path = "/foo";
+        String expected;
+        
+        public AsyncCB(ZooKeeper zk, CountDownLatch latch) {
+            this.zk = zk;
+            this.latch = latch;
+        }
+        
+        public void setRC(int rc) {
+            this.rc = rc;
+        }
+        
+        public void setPath(String path) {
+            this.path = path;
+        }
+        
+        public void processResult(int rc, String path, Object ctx)
+        {
+            this.rc = rc;
+            this.path = path;
+            this.expected = (String)ctx;
+            latch.countDown();
+        }
+        
+        /** String format is rc:path:<suffix> where <suffix> is defined by each
+         * subclass individually. */
+        @Override
+        public String toString() {
+            return rc + ":" + path + ":"; 
+        }
+
+        protected void verify() {
+            try {
+                latch.await(defaultTimeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                fail("unexpected interrupt");
+            }
+            // on the lookout for timeout
+            assertSame(latch.getCount(), 0L);
+            
+            String actual = toString();
+            
+            assertEquals(expected, actual);
+        }
+    }
+    
+    public static class StringCB extends AsyncCB implements StringCallback {
+        byte[] data = new byte[10];
+        List<ACL> acl = Ids.CREATOR_ALL_ACL;
+        int flags = 0;
+        String name = path;
+        
+        StringCB(ZooKeeper zk) {
+            this(zk, new CountDownLatch(1));
+        }
+        
+        StringCB(ZooKeeper zk, CountDownLatch latch) {
+            super(zk, latch);
+        }
+        
+        public void setPath(String path) {
+            super.setPath(path);
+            this.name = path;
+        }
+        
+        public String nodeName() {
+            return path.substring(path.lastIndexOf('/') + 1);
+        }
+        
+        public void processResult(int rc, String path, Object ctx, String name)
+        {
+            this.name = name;
+            super.processResult(rc, path, ctx);
+        }
+
+        public AsyncCB create() {
+            zk.create(path, data, acl, flags, this, toString());
+            return this;
+        }
+        
+        public void verifyCreate() {
+            create();
+            verify();
+        }
+        
+        public void verifyCreateFailure_NodeExists() {
+            new StringCB(zk).verifyCreate();
+            
+            rc = Code.NodeExists;
+            name = null;
+            zk.create(path, data, acl, flags, this, toString());
+            verify();
+        }
+        
+        @Override
+        public String toString() {
+            return super.toString() + name; 
+        }
+    }
+
+    public static class ACLCB extends AsyncCB implements ACLCallback {
+        List<ACL> acl = Ids.CREATOR_ALL_ACL;
+        int version = 0;
+        Stat stat = new Stat();
+        byte[] data = "testing".getBytes();
+        
+        ACLCB(ZooKeeper zk) {
+            this(zk, new CountDownLatch(1));
+        }
+
+        ACLCB(ZooKeeper zk, CountDownLatch latch) {
+            super(zk, latch);
+            stat.setAversion(0);
+            stat.setCversion(0);
+            stat.setEphemeralOwner(0);
+            stat.setVersion(0);
+        }
+
+        public void processResult(int rc, String path, Object ctx,
+                List<ACL> acl, Stat stat)
+        {
+            this.acl = acl;
+            this.stat = stat;
+            super.processResult(rc, path, ctx);
+        }
+        
+        public void verifyGetACL() {
+            new StringCB(zk).verifyCreate();
+
+            zk.getACL(path, stat, this, toString());
+            verify();
+        }
+        
+        public String toString(List<ACL> acls) {
+            StringBuffer result = new StringBuffer();
+            for(ACL acl : acls) {
+                result.append(acl.getPerms() + "::");
+            }
+            return result.toString();
+        }
+        
+        @Override
+        public String toString() {
+            return super.toString() + toString(acl) + ":" 
+                + ":" + version + ":" + new String(data)
+                + ":" + (stat == null ? "null" : stat.getAversion() + ":" 
+                        + stat.getCversion() + ":" + stat.getEphemeralOwner()
+                        + ":" + stat.getVersion()); 
+        }
+    }
+
+    public static class ChildrenCB extends AsyncCB implements ChildrenCallback {
+        List<String> children = new ArrayList<String>();
+        
+        ChildrenCB(ZooKeeper zk) {
+            this(zk, new CountDownLatch(1));
+        }
+
+        ChildrenCB(ZooKeeper zk, CountDownLatch latch) {
+            super(zk, latch);
+        }
+        
+        public void processResult(int rc, String path, Object ctx,
+                List<String> children)
+        {
+            this.children =
+                (children == null ? new ArrayList<String>() : children);
+            super.processResult(rc, path, ctx);
+        }
+        
+        public StringCB createNode() {
+            StringCB parent = new StringCB(zk);
+            parent.verifyCreate();
+
+            return parent;
+        }
+        
+        public StringCB createNode(StringCB parent) {
+            String childName = "bar";
+
+            return createNode(parent, childName);
+        }
+
+        public StringCB createNode(StringCB parent, String childName) {
+            StringCB child = new StringCB(zk);
+            child.setPath(parent.path + "/" + childName);
+            child.verifyCreate();
+            
+            return child;
+        }
+        
+        public void verifyGetChildrenEmpty() {
+            StringCB parent = createNode();
+
+            path = parent.path;
+            verify();
+        }
+        
+        public void verifyGetChildrenSingle() {
+            StringCB parent = createNode();
+            StringCB child = createNode(parent);
+
+            path = parent.path;
+            children.add(child.nodeName());
+            
+            verify();
+        }
+        
+        public void verifyGetChildrenTwo() {
+            StringCB parent = createNode();
+            StringCB child1 = createNode(parent, "child1");
+            StringCB child2 = createNode(parent, "child2");
+
+            path = parent.path;
+            children.add(child1.nodeName());
+            children.add(child2.nodeName());
+            
+            verify();
+        }
+        
+        public void verifyGetChildrenFailure_NoNode() {
+            rc = KeeperException.Code.NoNode;
+            verify();
+        }
+        
+        @Override
+        public void verify() {
+            zk.getChildren(path, false, this, toString());
+
+            super.verify();
+        }
+        
+        @Override
+        public String toString() {
+            return super.toString() + children.toString(); 
+        }
+    }
+
+    public static class DataCB extends AsyncCB implements DataCallback {
+        byte[] data = new byte[10];
+        Stat stat = new Stat();
+        
+        DataCB(ZooKeeper zk) {
+            this(zk, new CountDownLatch(1));
+        }
+
+        DataCB(ZooKeeper zk, CountDownLatch latch) {
+            super(zk, latch);
+            stat.setAversion(0);
+            stat.setCversion(0);
+            stat.setEphemeralOwner(0);
+            stat.setVersion(0);
+        }
+        
+        public void processResult(int rc, String path, Object ctx, byte[] data,
+                Stat stat)
+        {
+            this.data = data;
+            this.stat = stat;
+            super.processResult(rc, path, ctx);
+        }
+        
+        public void verifyGetData() {
+            new StringCB(zk).verifyCreate();
+
+            zk.getData(path, false, this, toString());
+            verify();
+        }
+        
+        public void verifyGetDataFailure_NoNode() {
+            rc = KeeperException.Code.NoNode;
+            data = null;
+            stat = null;
+            zk.getData(path, false, this, toString());
+            verify();
+        }
+        
+        @Override
+        public String toString() {
+            return super.toString()
+                + ":" + (data == null ? "null" : new String(data))
+                + ":" + (stat == null ? "null" : stat.getAversion() + ":" 
+                    + stat.getCversion() + ":" + stat.getEphemeralOwner()
+                    + ":" + stat.getVersion()); 
+        }
+    }
+
+    public static class StatCB extends AsyncCB implements StatCallback {
+        List<ACL> acl = Ids.CREATOR_ALL_ACL;
+        int version = 0;
+        Stat stat = new Stat();
+        byte[] data = "testing".getBytes();
+        
+        StatCB(ZooKeeper zk) {
+            this(zk, new CountDownLatch(1));
+        }
+
+        StatCB(ZooKeeper zk, CountDownLatch latch) {
+            super(zk, latch);
+            stat.setAversion(0);
+            stat.setCversion(0);
+            stat.setEphemeralOwner(0);
+            stat.setVersion(0);
+        }
+        
+        public void processResult(int rc, String path, Object ctx, Stat stat) {
+            this.stat = stat;
+            super.processResult(rc, path, ctx);
+        }
+        
+        public void verifySetACL() {
+            stat.setAversion(1);
+            new StringCB(zk).verifyCreate();
+
+            zk.setACL(path, acl, version, this, toString());
+            verify();
+        }
+        
+        public void verifySetACLFailure_NoNode() {
+            rc = KeeperException.Code.NoNode;
+            stat = null;
+            zk.setACL(path, acl, version, this, toString());
+            verify();
+        }
+        
+        public void verifySetData() {
+            stat.setVersion(1);
+            new StringCB(zk).verifyCreate();
+
+            zk.setData(path, data, version, this, toString());
+            verify();
+        }
+        
+        public void verifySetDataFailure_NoNode() {
+            rc = KeeperException.Code.NoNode;
+            stat = null;
+            zk.setData(path, data, version, this, toString());
+            verify();
+        }
+        
+        public void verifyExists() {
+            new StringCB(zk).verifyCreate();
+
+            zk.exists(path, false, this, toString());
+            verify();
+        }
+        
+        public void verifyExistsFailure_NoNode() {
+            rc = KeeperException.Code.NoNode;
+            stat = null;
+            zk.exists(path, false, this, toString());
+            verify();
+        }
+        
+        @Override
+        public String toString() {
+            return super.toString() + version
+                + ":" + new String(data)
+                + ":" + (stat == null ? "null" : stat.getAversion() + ":" 
+                        + stat.getCversion() + ":" + stat.getEphemeralOwner()
+                        + ":" + stat.getVersion()); 
+        }
+    }
+
+    public static class VoidCB extends AsyncCB implements VoidCallback {
+        int version = 0;
+        
+        VoidCB(ZooKeeper zk) {
+            this(zk, new CountDownLatch(1));
+        }
+        
+        VoidCB(ZooKeeper zk, CountDownLatch latch) {
+            super(zk, latch);
+        }
+        
+        public void verifyDelete() {
+            new StringCB(zk).verifyCreate();
+
+            zk.delete(path, version, this, toString());
+            verify();
+        }
+        
+        public void verifyDeleteFailure_NoNode() {
+            rc = Code.NoNode;
+            zk.delete(path, version, this, toString());
+            verify();
+        }
+        
+        public void verifySync() {
+            zk.sync(path, this, toString());
+            verify();
+        }
+        
+        @Override
+        public String toString() {
+            return super.toString() + version; 
+        }
+    }
+
+
+}

Added: hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java?rev=692527&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java (added)
+++ hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java Fri Sep  5 13:20:20 2008
@@ -0,0 +1,178 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.zookeeper.test;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.log4j.Logger;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.test.AsyncOps.ACLCB;
+import org.apache.zookeeper.test.AsyncOps.ChildrenCB;
+import org.apache.zookeeper.test.AsyncOps.DataCB;
+import org.apache.zookeeper.test.AsyncOps.StatCB;
+import org.apache.zookeeper.test.AsyncOps.StringCB;
+import org.apache.zookeeper.test.AsyncOps.VoidCB;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Functional testing of asynchronous operations, both positive and negative
+ * testing.
+ * 
+ * This just scratches the surface, but exercises the basic async functionality.
+ */
+public class AsyncOpsTest extends ClientBase {
+    private static final Logger LOG = Logger.getLogger(AsyncOpsTest.class);
+
+    private ZooKeeper zk;
+    
+    @Before
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        LOG.info("STARTING " + getName());
+
+        zk = createClient();
+        zk.addAuthInfo("digest", "ben:passwd".getBytes());
+    }
+
+    @After
+    @Override
+    protected void tearDown() throws Exception {
+        zk.close();
+        
+        super.tearDown();
+        
+        LOG.info("Test clients shutting down");
+
+        LOG.info("FINISHED " + getName());
+    }
+
+    @Test
+    public void testAsyncCreate() {
+        new StringCB(zk).verifyCreate();
+    }
+
+    @Test
+    public void testAsyncCreateThree() {
+        CountDownLatch latch = new CountDownLatch(3);
+        
+        StringCB op1 = new StringCB(zk, latch);
+        op1.setPath("/op1");
+        StringCB op2 = new StringCB(zk, latch);
+        op2.setPath("/op2");
+        StringCB op3 = new StringCB(zk, latch);
+        op3.setPath("/op3");
+        
+        op1.create();
+        op2.create();
+        op3.create();
+        
+        op1.verify();
+        op2.verify();
+        op3.verify();
+    }
+    
+    @Test
+    public void testAsyncCreateFailure_NodeExists() {
+        new StringCB(zk).verifyCreateFailure_NodeExists();
+    }
+
+    @Test
+    public void testAsyncDelete() {
+        new VoidCB(zk).verifyDelete();
+    }
+
+    @Test
+    public void testAsyncDeleteFailure_NoNode() {
+        new VoidCB(zk).verifyDeleteFailure_NoNode();
+    }
+
+    @Test
+    public void testAsyncSync() {
+        new VoidCB(zk).verifySync();
+    }
+
+    @Test
+    public void testAsyncSetACL() {
+        new StatCB(zk).verifySetACL();
+    }
+
+    @Test
+    public void testAsyncSetACLFailure_NoNode() {
+        new StatCB(zk).verifySetACLFailure_NoNode();
+    }
+
+    @Test
+    public void testAsyncSetData() {
+        new StatCB(zk).verifySetData();
+    }
+
+    @Test
+    public void testAsyncSetDataFailure_NoNode() {
+        new StatCB(zk).verifySetDataFailure_NoNode();
+    }
+
+    @Test
+    public void testAsyncExists() {
+        new StatCB(zk).verifySetData();
+    }
+
+    @Test
+    public void testAsyncExistsFailure_NoNode() {
+        new StatCB(zk).verifySetData();
+    }
+
+    @Test
+    public void testAsyncGetACL() {
+        new ACLCB(zk).verifyGetACL();
+    }
+
+    @Test
+    public void testAsyncGetChildrenEmpty() {
+        new ChildrenCB(zk).verifyGetChildrenEmpty();
+    }
+
+    @Test
+    public void testAsyncGetChildrenSingle() {
+        new ChildrenCB(zk).verifyGetChildrenSingle();
+    }
+
+    @Test
+    public void testAsyncGetChildrenTwo() {
+        new ChildrenCB(zk).verifyGetChildrenTwo();
+    }
+
+    @Test
+    public void testAsyncGetChildrenFailure_NoNode() {
+        new ChildrenCB(zk).verifyGetChildrenFailure_NoNode();
+    }
+
+    @Test
+    public void testAsyncGetData() {
+        new DataCB(zk).verifyGetData();
+    }
+
+    @Test
+    public void testAsyncGetDataFailure_NoNode() {
+        new DataCB(zk).verifyGetDataFailure_NoNode();
+    }
+}