/*
* 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.accumulo.fate.zookeeper;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import org.apache.accumulo.fate.zookeeper.IZooReaderWriter.Mutator;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.BadVersionException;
import org.apache.zookeeper.KeeperException.Code;
import org.apache.zookeeper.KeeperException.ConnectionLossException;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.zookeeper.KeeperException.NodeExistsException;
import org.apache.zookeeper.KeeperException.SessionExpiredException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ZooReaderWriterTest {
private ZooReaderWriter zrw;
private ZooKeeper zk;
private RetryFactory retryFactory;
private Retry retry;
@Before
public void setup() {
zk = EasyMock.createMock(ZooKeeper.class);
zrw = EasyMock.createMockBuilder(ZooReaderWriter.class).addMockedMethods("getRetryFactory", "getZooKeeper").createMock();
retryFactory = EasyMock.createMock(RetryFactory.class);
retry = EasyMock.createMock(Retry.class);
EasyMock.expect(zrw.getZooKeeper()).andReturn(zk).anyTimes();
EasyMock.expect(zrw.getRetryFactory()).andReturn(retryFactory).anyTimes();
EasyMock.expect(retryFactory.create()).andReturn(retry).anyTimes();
}
@Test(expected = NoNodeException.class)
public void testDeleteFailOnInitialNoNode() throws Exception {
final String path = "/foo";
final int version = -1;
zk.delete(path, version);
EasyMock.expectLastCall().andThrow(KeeperException.create(Code.NONODE));
EasyMock.expect(retry.hasRetried()).andReturn(false);
EasyMock.replay(zk, zrw, retryFactory, retry);
zrw.delete(path, version);
}
@Test
public void testDeleteFailOnRetry() throws Exception {
final String path = "/foo";
final int version = -1;
zk.delete(path, version);
EasyMock.expectLastCall().andThrow(KeeperException.create(Code.CONNECTIONLOSS));
EasyMock.expect(retry.canRetry()).andReturn(true);
retry.useRetry();
EasyMock.expectLastCall().once();
retry.waitForNextAttempt();
EasyMock.expectLastCall().once();
zk.delete(path, version);
EasyMock.expectLastCall().andThrow(KeeperException.create(Code.NONODE));
EasyMock.expect(retry.hasRetried()).andReturn(true);
EasyMock.replay(zk, zrw, retryFactory, retry);
zrw.delete(path, version);
EasyMock.verify(zk, zrw, retryFactory, retry);
}
@Test(expected = SessionExpiredException.class)
public void testMutateNodeCreationFails() throws Exception {
final String path = "/foo";
final byte[] value = new byte[] {0};
final List<ACL> acls = Collections.<ACL> emptyList();
Mutator mutator = new Mutator() {
@Override
public byte[] mutate(byte[] currentValue) throws Exception {
return new byte[] {1};
}
};
zk.create(path, value, acls, CreateMode.PERSISTENT);
EasyMock.expectLastCall().andThrow(new SessionExpiredException()).once();
EasyMock.expect(retry.canRetry()).andReturn(false);
EasyMock.expect(retry.retriesCompleted()).andReturn(1l).once();
EasyMock.replay(zk, zrw, retryFactory, retry);
zrw.mutate(path, value, acls, mutator);
}
@Test
public void testMutateWithBadVersion() throws Exception {
final String path = "/foo";
final byte[] value = new byte[] {0};
final List<ACL> acls = Collections.<ACL> emptyList();
final byte[] mutatedBytes = new byte[] {1};
Mutator mutator = new Mutator() {
@Override
public byte[] mutate(byte[] currentValue) throws Exception {
return mutatedBytes;
}
};
Method getDataMethod = ZooReaderWriter.class.getMethod("getData", String.class, boolean.class, Stat.class);
zrw = EasyMock.createMockBuilder(ZooReaderWriter.class).addMockedMethods("getRetryFactory", "getZooKeeper").addMockedMethod(getDataMethod).createMock();
EasyMock.expect(zrw.getRetryFactory()).andReturn(retryFactory).anyTimes();
EasyMock.expect(zrw.getZooKeeper()).andReturn(zk).anyTimes();
Stat stat = new Stat();
zk.create(path, value, acls, CreateMode.PERSISTENT);
EasyMock.expectLastCall().andThrow(new NodeExistsException()).once();
EasyMock.expect(zrw.getData(path, false, stat)).andReturn(new byte[] {3}).times(2);
// BadVersionException should retry
EasyMock.expect(zk.setData(path, mutatedBytes, 0)).andThrow(new BadVersionException());
// Let 2nd setData succeed
EasyMock.expect(zk.setData(path, mutatedBytes, 0)).andReturn(null);
EasyMock.replay(zk, zrw, retryFactory, retry);
Assert.assertArrayEquals(new byte[] {1}, zrw.mutate(path, value, acls, mutator));
EasyMock.verify(zk, zrw, retryFactory, retry);
}
@Test
public void testMutateWithRetryOnSetData() throws Exception {
final String path = "/foo";
final byte[] value = new byte[] {0};
final List<ACL> acls = Collections.<ACL> emptyList();
final byte[] mutatedBytes = new byte[] {1};
Mutator mutator = new Mutator() {
@Override
public byte[] mutate(byte[] currentValue) throws Exception {
return mutatedBytes;
}
};
Method getDataMethod = ZooReaderWriter.class.getMethod("getData", String.class, boolean.class, Stat.class);
zrw = EasyMock.createMockBuilder(ZooReaderWriter.class).addMockedMethods("getRetryFactory", "getZooKeeper").addMockedMethod(getDataMethod).createMock();
EasyMock.expect(zrw.getRetryFactory()).andReturn(retryFactory).anyTimes();
EasyMock.expect(zrw.getZooKeeper()).andReturn(zk).anyTimes();
Stat stat = new Stat();
zk.create(path, value, acls, CreateMode.PERSISTENT);
EasyMock.expectLastCall().andThrow(new NodeExistsException()).once();
EasyMock.expect(zrw.getData(path, false, stat)).andReturn(new byte[] {3}).times(2);
// BadVersionException should retry
EasyMock.expect(zk.setData(path, mutatedBytes, 0)).andThrow(new ConnectionLossException());
EasyMock.expect(retry.canRetry()).andReturn(true);
retry.useRetry();
EasyMock.expectLastCall();
retry.waitForNextAttempt();
EasyMock.expectLastCall();
// Let 2nd setData succeed
EasyMock.expect(zk.setData(path, mutatedBytes, 0)).andReturn(null);
EasyMock.replay(zk, zrw, retryFactory, retry);
Assert.assertArrayEquals(new byte[] {1}, zrw.mutate(path, value, acls, mutator));
EasyMock.verify(zk, zrw, retryFactory, retry);
}
}