http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
new file mode 100644
index 0000000..29f8bea
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
@@ -0,0 +1,36 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.log4j.Level;
+import org.apache.ratis.grpc.server.GrpcServerProtocolService;
+import org.apache.ratis.server.impl.RaftReconfigurationBaseTest;
+import org.apache.ratis.util.LogUtils;
+
+import java.io.IOException;
+
+public class TestRaftReconfigurationWithGrpc extends 
RaftReconfigurationBaseTest {
+  static {
+    LogUtils.setLogLevel(GrpcServerProtocolService.LOG, Level.DEBUG);
+  }
+
+  @Override
+  public MiniRaftClusterWithGrpc getCluster(int peerNum) {
+    return MiniRaftClusterWithGrpc.FACTORY.newCluster(peerNum, prop);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftServerWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftServerWithGrpc.java 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftServerWithGrpc.java
new file mode 100644
index 0000000..2ec7ae8
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftServerWithGrpc.java
@@ -0,0 +1,59 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.RaftTestUtil;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.server.impl.ServerImplUtils;
+import org.apache.ratis.statemachine.StateMachine;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.channels.OverlappingFileLockException;
+
+public class TestRaftServerWithGrpc extends BaseTest {
+
+  @Test
+  public void testServerRestartOnException() throws Exception {
+    RaftProperties properties = new RaftProperties();
+    final MiniRaftClusterWithGrpc cluster
+        = MiniRaftClusterWithGrpc.FACTORY.newCluster(1, properties);
+    cluster.start();
+    RaftPeerId leaderId = RaftTestUtil.waitForLeader(cluster).getId();
+    GrpcConfigKeys.Server.setPort(properties, 
cluster.getLeader().getServerRpc().getInetSocketAddress().getPort());
+    // Create a raft server proxy with server rpc bound to a different address
+    // compared to leader. This helps in locking the raft storage directory to
+    // be used by next raft server proxy instance.
+    final StateMachine stateMachine = cluster.getLeader().getStateMachine();
+    ServerImplUtils.newRaftServer(leaderId, cluster.getGroup(), gid -> 
stateMachine, properties, null);
+    // Close the server rpc for leader so that new raft server can be bound to 
it.
+    cluster.getLeader().getServerRpc().close();
+
+    // Create a raft server proxy with server rpc bound to same address as
+    // the leader. This step would fail as the raft storage has been locked by
+    // the raft server proxy created earlier. Raft server proxy should close
+    // the rpc server on failure.
+    testFailureCase("start a new server with the same address",
+        () -> ServerImplUtils.newRaftServer(leaderId, cluster.getGroup(), gid 
-> stateMachine, properties, null).start(),
+        IOException.class, OverlappingFileLockException.class);
+    // Try to start a raft server rpc at the leader address.
+    
cluster.getServer(leaderId).getFactory().newRaftServerRpc(cluster.getServer(leaderId));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftSnapshotWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftSnapshotWithGrpc.java 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftSnapshotWithGrpc.java
new file mode 100644
index 0000000..a960478
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftSnapshotWithGrpc.java
@@ -0,0 +1,28 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.statemachine.RaftSnapshotBaseTest;
+
+public class TestRaftSnapshotWithGrpc extends RaftSnapshotBaseTest {
+  @Override
+  public MiniRaftCluster.Factory<?> getFactory() {
+    return MiniRaftClusterWithGrpc.FACTORY;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftStateMachineExceptionWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftStateMachineExceptionWithGrpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftStateMachineExceptionWithGrpc.java
new file mode 100644
index 0000000..c8789a7
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftStateMachineExceptionWithGrpc.java
@@ -0,0 +1,26 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.ratis.server.impl.RaftStateMachineExceptionTests;
+
+public class TestRaftStateMachineExceptionWithGrpc
+    extends RaftStateMachineExceptionTests<MiniRaftClusterWithGrpc>
+    implements MiniRaftClusterWithGrpc.FactoryGet {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftStream.java
----------------------------------------------------------------------
diff --git a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftStream.java 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftStream.java
new file mode 100644
index 0000000..ba31b2b
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftStream.java
@@ -0,0 +1,311 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.log4j.Level;
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.RaftTestUtil;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.grpc.client.GrpcClientStreamer;
+import org.apache.ratis.grpc.client.GrpcOutputStream;
+import org.apache.ratis.protocol.ClientId;
+import org.apache.ratis.server.impl.RaftServerImpl;
+import org.apache.ratis.server.protocol.TermIndex;
+import org.apache.ratis.server.storage.RaftLog;
+import org.apache.ratis.proto.RaftProtos;
+import org.apache.ratis.util.LogUtils;
+import org.apache.ratis.util.SizeInBytes;
+import org.apache.ratis.util.StringUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import static org.apache.ratis.RaftTestUtil.waitForLeader;
+import static org.junit.Assert.fail;
+
+@Ignore
+public class TestRaftStream extends BaseTest {
+  static {
+    LogUtils.setLogLevel(GrpcClientStreamer.LOG, Level.ALL);
+  }
+
+  private static final RaftProperties prop = new RaftProperties();
+  private static final int NUM_SERVERS = 3;
+  private static final byte[] BYTES = new byte[4];
+
+  private MiniRaftClusterWithGrpc cluster;
+
+  @After
+  public void tearDown() {
+    if (cluster != null) {
+      cluster.shutdown();
+    }
+  }
+
+  private byte[] toBytes(int i) {
+    return toBytes(i, BYTES);
+  }
+  private byte[] toBytes(int i, byte[] b) {
+    b[0] = (byte) ((i >>> 24) & 0xFF);
+    b[1] = (byte) ((i >>> 16) & 0xFF);
+    b[2] = (byte) ((i >>> 8) & 0xFF);
+    b[3] = (byte) (i & 0xFF);
+    return b;
+  }
+
+  @Test
+  public void testSimpleWrite() throws Exception {
+    final int numRequests = 500;
+    LOG.info("Running testSimpleWrite, numRequests=" + numRequests);
+
+    // default 64K is too large for a test
+    GrpcConfigKeys.OutputStream.setBufferSize(prop, SizeInBytes.valueOf(4));
+    cluster = MiniRaftClusterWithGrpc.FACTORY.newCluster(NUM_SERVERS, prop);
+
+    cluster.start();
+    RaftServerImpl leader = waitForLeader(cluster);
+
+    try (GrpcOutputStream out = new GrpcOutputStream(prop, ClientId.randomId(),
+        cluster.getGroup(), leader.getId())) {
+      for (int i = 0; i < numRequests; i++) { // generate requests
+        out.write(toBytes(i));
+      }
+    }
+
+    // check the leader's raft log
+    final RaftLog raftLog = leader.getState().getLog();
+    final AtomicInteger i = new AtomicInteger();
+    checkLog(raftLog, numRequests, () -> toBytes(i.getAndIncrement()));
+  }
+
+  private void checkLog(RaftLog raftLog, long expectedCommittedIndex,
+      Supplier<byte[]> s) throws IOException {
+    long committedIndex = raftLog.getLastCommittedIndex();
+    Assert.assertEquals(expectedCommittedIndex, committedIndex);
+    // check the log content
+    TermIndex[] entries = raftLog.getEntries(1, expectedCommittedIndex + 1);
+    for (TermIndex entry : entries) {
+      RaftProtos.LogEntryProto log  = raftLog.get(entry.getIndex());
+      byte[] logData = 
log.getStateMachineLogEntry().getLogData().toByteArray();
+      byte[] expected = s.get();
+      LOG.info("log " + entry + " " + log.getLogEntryBodyCase() + " " + 
StringUtils.bytes2HexString(logData));
+      Assert.assertEquals(expected.length, logData.length);
+      Assert.assertArrayEquals(expected, logData);
+    }
+  }
+
+  @Test
+  public void testWriteAndFlush() throws Exception {
+    LOG.info("Running testWriteAndFlush");
+
+    GrpcConfigKeys.OutputStream.setBufferSize(prop, 
SizeInBytes.valueOf(ByteValue.BUFFERSIZE));
+    cluster = MiniRaftClusterWithGrpc.FACTORY.newCluster(NUM_SERVERS, prop);
+    cluster.start();
+
+    RaftServerImpl leader = waitForLeader(cluster);
+    GrpcOutputStream out = new GrpcOutputStream(prop, ClientId.randomId(),
+        cluster.getGroup(), leader.getId());
+
+    int[] lengths = new int[]{1, 500, 1023, 1024, 1025, 2048, 3000, 3072};
+    ByteValue[] values = new ByteValue[lengths.length];
+    for (int i = 0; i < values.length; i++) {
+      values[i] = new ByteValue(lengths[i], (byte) 9);
+    }
+
+    List<byte[]> expectedTxs = new ArrayList<>();
+    for (ByteValue v : values) {
+      byte[] data = v.genData();
+      expectedTxs.addAll(v.getTransactions());
+      out.write(data);
+      out.flush();
+
+      // make sure after the flush the data has been committed
+      Assert.assertEquals(expectedTxs.size(),
+          leader.getState().getLastAppliedIndex());
+    }
+    out.close();
+
+    try {
+      out.write(0);
+      fail("The OutputStream has been closed");
+    } catch (IOException ignored) {
+    }
+
+    LOG.info("Start to check leader's log");
+    final AtomicInteger index = new AtomicInteger(0);
+    checkLog(leader.getState().getLog(), expectedTxs.size(),
+        () -> expectedTxs.get(index.getAndIncrement()));
+  }
+
+  private static class ByteValue {
+    final static int BUFFERSIZE = 1024;
+
+    final int length;
+    final byte value;
+    final int numTx;
+    byte[] data;
+
+    ByteValue(int length, byte value) {
+      this.length = length;
+      this.value = value;
+      numTx = (length - 1) / BUFFERSIZE + 1;
+    }
+
+    byte[] genData() {
+      data = new byte[length];
+      Arrays.fill(data, value);
+      return data;
+    }
+
+    Collection<byte[]> getTransactions() {
+      if (data.length <= BUFFERSIZE) {
+        return Collections.singletonList(data);
+      } else {
+        List<byte[]> list = new ArrayList<>();
+        for (int i = 0; i < numTx; i++) {
+          int txSize = Math.min(BUFFERSIZE, length - BUFFERSIZE * i);
+          byte[] t = new byte[txSize];
+          Arrays.fill(t, value);
+          list.add(t);
+        }
+        return list;
+      }
+    }
+  }
+
+  @Test
+  public void testWriteWithOffset() throws Exception {
+    LOG.info("Running testWriteWithOffset");
+    GrpcConfigKeys.OutputStream.setBufferSize(prop, 
SizeInBytes.valueOf(ByteValue.BUFFERSIZE));
+
+    cluster = MiniRaftClusterWithGrpc.FACTORY.newCluster(NUM_SERVERS, prop);
+    cluster.start();
+    RaftServerImpl leader = waitForLeader(cluster);
+
+    GrpcOutputStream out = new GrpcOutputStream(prop, ClientId.randomId(),
+        cluster.getGroup(), leader.getId());
+
+    byte[] b1 = new byte[ByteValue.BUFFERSIZE / 2];
+    Arrays.fill(b1, (byte) 1);
+    byte[] b2 = new byte[ByteValue.BUFFERSIZE];
+    Arrays.fill(b2, (byte) 2);
+    byte[] b3 = new byte[ByteValue.BUFFERSIZE * 2 + ByteValue.BUFFERSIZE / 2];
+    Arrays.fill(b3, (byte) 3);
+    byte[] b4 = new byte[ByteValue.BUFFERSIZE * 4];
+    Arrays.fill(b3, (byte) 4);
+
+    byte[] expected = new byte[ByteValue.BUFFERSIZE * 8];
+    byte[][] data = new byte[][]{b1, b2, b3, b4};
+    final Random random = new Random();
+    int totalSize = 0;
+    for (byte[] b : data) {
+      System.arraycopy(b, 0, expected, totalSize, b.length);
+      totalSize += b.length;
+
+      int written = 0;
+      while (written < b.length) {
+        int toWrite = random.nextInt(b.length - written) + 1;
+        LOG.info("write {} bytes", toWrite);
+        out.write(b, written, toWrite);
+        written += toWrite;
+      }
+    }
+    out.close();
+
+    final RaftLog log = leader.getState().getLog();
+    // 0.5 + 1 + 2.5 + 4 = 8
+    Assert.assertEquals(8, leader.getState().getLastAppliedIndex());
+    Assert.assertEquals(8, log.getLastCommittedIndex());
+    TermIndex[] entries = log.getEntries(1, 9);
+    byte[] actual = new byte[ByteValue.BUFFERSIZE * 8];
+    totalSize = 0;
+    for (TermIndex e : entries) {
+      byte[] eValue = 
log.get(e.getIndex()).getStateMachineLogEntry().getLogData().toByteArray();
+      Assert.assertEquals(ByteValue.BUFFERSIZE, eValue.length);
+      System.arraycopy(eValue, 0, actual, totalSize, eValue.length);
+      totalSize += eValue.length;
+    }
+    Assert.assertArrayEquals(expected, actual);
+  }
+
+  /**
+   * Write while leader is killed
+   */
+  @Test
+  public void testKillLeader() throws Exception {
+    LOG.info("Running testChangeLeader");
+
+    GrpcConfigKeys.OutputStream.setBufferSize(prop, SizeInBytes.valueOf(4));
+    cluster = MiniRaftClusterWithGrpc.FACTORY.newCluster(NUM_SERVERS, prop);
+    cluster.start();
+    final RaftServerImpl leader = waitForLeader(cluster);
+
+    final AtomicBoolean running  = new AtomicBoolean(true);
+    final AtomicReference<Boolean> success = new AtomicReference<>();
+    final AtomicInteger result = new AtomicInteger(0);
+    final CountDownLatch latch = new CountDownLatch(1);
+
+    new Thread(() -> {
+      LOG.info("Writer thread starts");
+      int count = 0;
+      try (GrpcOutputStream out = new GrpcOutputStream(prop, 
ClientId.randomId(),
+          cluster.getGroup(), leader.getId())) {
+        while (running.get()) {
+          out.write(toBytes(count++));
+          Thread.sleep(10);
+        }
+        success.set(true);
+        result.set(count);
+      } catch (Exception e) {
+        LOG.info("Got exception when writing", e);
+        success.set(false);
+      } finally {
+        latch.countDown();
+      }
+    }).start();
+
+    // force change the leader
+    Thread.sleep(500);
+    RaftTestUtil.waitAndKillLeader(cluster);
+    final RaftServerImpl newLeader = waitForLeader(cluster);
+    Assert.assertNotEquals(leader.getId(), newLeader.getId());
+    Thread.sleep(500);
+
+    running.set(false);
+    latch.await(5, TimeUnit.SECONDS);
+    LOG.info("Writer success? " + success.get());
+    Assert.assertTrue(success.get());
+    // total number of tx should be >= result + 2, where 2 means two NoOp from
+    // leaders. It may be larger than result+2 because the client may resend
+    // requests and we do not have retry cache on servers yet.
+    LOG.info("last applied index: {}. total number of requests: {}",
+        newLeader.getState().getLastAppliedIndex(), result.get());
+    Assert.assertTrue(
+        newLeader.getState().getLastAppliedIndex() >= result.get() + 1);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftWithGrpc.java 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftWithGrpc.java
new file mode 100644
index 0000000..d98be53
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftWithGrpc.java
@@ -0,0 +1,110 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.RaftBasicTests;
+import org.apache.ratis.RaftTestUtil;
+import org.apache.ratis.client.RaftClient;
+import org.apache.ratis.protocol.RaftClientReply;
+import org.apache.ratis.server.impl.BlockRequestHandlingInjection;
+import org.apache.ratis.server.impl.RaftServerTestUtil;
+import org.apache.ratis.server.protocol.TermIndex;
+import org.apache.ratis.statemachine.SimpleStateMachine4Testing;
+import org.apache.ratis.statemachine.StateMachine;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+
+import static org.apache.ratis.RaftTestUtil.waitForLeader;
+
+public class TestRaftWithGrpc
+    extends RaftBasicTests<MiniRaftClusterWithGrpc>
+    implements MiniRaftClusterWithGrpc.FactoryGet {
+
+  {
+    getProperties().setClass(MiniRaftCluster.STATEMACHINE_CLASS_KEY,
+        SimpleStateMachine4Testing.class, StateMachine.class);
+  }
+
+  @Override
+  @Test
+  public void testWithLoad() throws Exception {
+    super.testWithLoad();
+    BlockRequestHandlingInjection.getInstance().unblockAll();
+  }
+
+  @Test
+  public void testRequestTimeout() throws Exception {
+    try(MiniRaftClusterWithGrpc cluster = newCluster(NUM_SERVERS)) {
+      cluster.start();
+      testRequestTimeout(false, cluster, LOG);
+    }
+  }
+
+  @Test
+  public void testUpdateViaHeartbeat() throws Exception {
+    LOG.info("Running testUpdateViaHeartbeat");
+    final MiniRaftClusterWithGrpc cluster = newCluster(NUM_SERVERS);
+    cluster.start();
+    waitForLeader(cluster);
+    long waitTime = 5000;
+    try (final RaftClient client = cluster.createClient()) {
+      // block append requests
+      cluster.getServerAliveStream()
+          .filter(impl -> !impl.isLeader())
+          .map(SimpleStateMachine4Testing::get)
+          .forEach(SimpleStateMachine4Testing::blockWriteStateMachineData);
+
+      CompletableFuture<RaftClientReply>
+          replyFuture = client.sendAsync(new 
RaftTestUtil.SimpleMessage("abc"));
+      Thread.sleep(waitTime);
+      // replyFuture should not be completed until append request is unblocked.
+      Assert.assertTrue(!replyFuture.isDone());
+      // unblock append request.
+      cluster.getServerAliveStream()
+          .filter(impl -> !impl.isLeader())
+          .map(SimpleStateMachine4Testing::get)
+          .forEach(SimpleStateMachine4Testing::unblockWriteStateMachineData);
+
+      long index = cluster.getLeader().getState().getLog().getNextIndex();
+      TermIndex[] leaderEntries = 
cluster.getLeader().getState().getLog().getEntries(0, Integer.MAX_VALUE);
+      // The entries have been appended in the followers
+      // although the append entry timed out at the leader
+      cluster.getServerAliveStream().forEach(raftServer -> {
+        Assert.assertEquals(raftServer.getState().getLog().getNextIndex(), 
index);
+        if (!raftServer.isLeader()) {
+          TermIndex[] serverEntries = 
raftServer.getState().getLog().getEntries(0, Integer.MAX_VALUE);
+          Assert.assertArrayEquals(serverEntries, leaderEntries);
+        }
+      });
+
+      // Wait for heartbeats from leader to be received by followers
+      Thread.sleep(500);
+      
RaftServerTestUtil.getLogAppenders(cluster.getLeader()).forEach(logAppender -> {
+        // FollowerInfo in the leader state should have updated next and match 
index.
+        final long followerMatchIndex = 
logAppender.getFollower().getMatchIndex();
+        Assert.assertTrue(followerMatchIndex >= index - 1);
+        Assert.assertEquals(followerMatchIndex + 1, 
logAppender.getFollower().getNextIndex());
+      });
+    }
+    cluster.shutdown();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestRetryCacheWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRetryCacheWithGrpc.java 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRetryCacheWithGrpc.java
new file mode 100644
index 0000000..cd04b43
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRetryCacheWithGrpc.java
@@ -0,0 +1,115 @@
+/**
+ * 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.ratis.grpc;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.log4j.Level;
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.RaftTestUtil;
+import org.apache.ratis.RetryCacheTests;
+import org.apache.ratis.client.RaftClient;
+import org.apache.ratis.client.RaftClientRpc;
+import org.apache.ratis.protocol.ClientId;
+import org.apache.ratis.protocol.RaftClientRequest;
+import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.server.impl.RaftServerImpl;
+import org.apache.ratis.proto.RaftProtos;
+import org.apache.ratis.util.LogUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestRetryCacheWithGrpc extends RetryCacheTests {
+  static {
+    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
+  }
+
+  private final MiniRaftClusterWithGrpc cluster;
+
+  public TestRetryCacheWithGrpc() throws IOException {
+    cluster = MiniRaftClusterWithGrpc.FACTORY.newCluster(
+        NUM_SERVERS, properties);
+    Assert.assertNull(cluster.getLeader());
+  }
+
+  @Override
+  public MiniRaftClusterWithGrpc getCluster() {
+    return cluster;
+  }
+
+  @Test
+  public void testAsyncRetryWithReplicatedAll() throws Exception {
+    final MiniRaftCluster cluster = getCluster();
+    RaftTestUtil.waitForLeader(cluster);
+
+    final RaftPeerId leaderId = cluster.getLeaderAndSendFirstMessage().getId();
+    long oldLastApplied = cluster.getLeader().getState().getLastAppliedIndex();
+
+    // Kill a follower
+    final RaftPeerId killedFollower = cluster.getFollowers().get(0).getId();
+    cluster.killServer(killedFollower);
+
+    final long callId = 999;
+    final long seqNum = 111;
+    final ClientId clientId = ClientId.randomId();
+
+    // Retry with the same clientId and callId
+    final List<CompletableFuture<RaftClient>> futures = new ArrayList<>();
+    futures.addAll(sendRetry(clientId, leaderId, callId, seqNum, cluster));
+    futures.addAll(sendRetry(clientId, leaderId, callId, seqNum, cluster));
+
+    // restart the killed follower
+    cluster.restartServer(killedFollower, false);
+    for(CompletableFuture<RaftClient> f : futures) {
+      f.join().close();
+    }
+    assertServer(cluster, clientId, callId, oldLastApplied);
+  }
+
+  List<CompletableFuture<RaftClient>> sendRetry(
+      ClientId clientId, RaftPeerId leaderId, long callId, long seqNum, 
MiniRaftCluster cluster)
+      throws Exception {
+    List<CompletableFuture<RaftClient>> futures = new ArrayList<>();
+    final int numRequest = 3;
+    for (int i = 0; i < numRequest; i++) {
+      final RaftClient client = cluster.createClient(leaderId, 
cluster.getGroup(), clientId);
+      final RaftClientRpc rpc = client.getClientRpc();
+      final RaftClientRequest request = 
cluster.newRaftClientRequest(client.getId(), leaderId,
+          callId, seqNum, new RaftTestUtil.SimpleMessage("message"), 
RaftProtos.ReplicationLevel.ALL);
+
+      LOG.info("{} sendRequestAsync {}", i, request);
+      futures.add(rpc.sendRequestAsync(request)
+          .thenApply(reply -> assertReply(reply, client, callId)));
+    }
+
+    for(CompletableFuture<RaftClient> f : futures) {
+      try {
+        f.get(200, TimeUnit.MILLISECONDS);
+        Assert.fail("It should timeout for ReplicationLevel.ALL since a 
follower is down");
+      } catch(TimeoutException te) {
+        LOG.info("Expected " + te);
+      }
+    }
+    return futures;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestServerInformationWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestServerInformationWithGrpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestServerInformationWithGrpc.java
new file mode 100644
index 0000000..30be724
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestServerInformationWithGrpc.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.ratis.server.impl.ServerInformationBaseTest;
+
+public class TestServerInformationWithGrpc
+    extends ServerInformationBaseTest<MiniRaftClusterWithGrpc>
+    implements MiniRaftClusterWithGrpc.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestServerRestartWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestServerRestartWithGrpc.java 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestServerRestartWithGrpc.java
new file mode 100644
index 0000000..682b3ba
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestServerRestartWithGrpc.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.ratis.server.ServerRestartTests;
+
+public class TestServerRestartWithGrpc
+    extends ServerRestartTests<MiniRaftClusterWithGrpc>
+    implements MiniRaftClusterWithGrpc.FactoryGet {
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestStateMachineShutdownWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestStateMachineShutdownWithGrpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestStateMachineShutdownWithGrpc.java
new file mode 100644
index 0000000..d8d1d0d
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestStateMachineShutdownWithGrpc.java
@@ -0,0 +1,26 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.ratis.server.impl.StateMachineShutdownTests;
+
+public class TestStateMachineShutdownWithGrpc
+    extends StateMachineShutdownTests<MiniRaftClusterWithGrpc>
+    implements MiniRaftClusterWithGrpc.FactoryGet{
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/grpc/TestWatchRequestWithGrpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/TestWatchRequestWithGrpc.java 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestWatchRequestWithGrpc.java
new file mode 100644
index 0000000..7b9061b
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/grpc/TestWatchRequestWithGrpc.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.grpc;
+
+import org.apache.ratis.WatchRequestTests;
+
+public class TestWatchRequestWithGrpc
+    extends WatchRequestTests<MiniRaftClusterWithGrpc>
+    implements MiniRaftClusterWithGrpc.FactoryGet {
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestGroupManagementWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestGroupManagementWithNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestGroupManagementWithNetty.java
new file mode 100644
index 0000000..e049e32
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestGroupManagementWithNetty.java
@@ -0,0 +1,28 @@
+/**
+ * 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.ratis.netty;
+
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.server.impl.GroupManagementBaseTest;
+
+public class TestGroupManagementWithNetty extends GroupManagementBaseTest {
+  @Override
+  public MiniRaftCluster.Factory<? extends MiniRaftCluster> 
getClusterFactory() {
+    return MiniRaftClusterWithNetty.FACTORY;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestLeaderElectionWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestLeaderElectionWithNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestLeaderElectionWithNetty.java
new file mode 100644
index 0000000..6d40b60
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestLeaderElectionWithNetty.java
@@ -0,0 +1,35 @@
+/**
+ * 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.ratis.netty;
+
+import org.apache.ratis.server.impl.BlockRequestHandlingInjection;
+import org.apache.ratis.server.impl.LeaderElectionTests;
+import org.junit.Test;
+
+public class TestLeaderElectionWithNetty
+    extends LeaderElectionTests<MiniRaftClusterWithNetty>
+    implements MiniRaftClusterWithNetty.FactoryGet {
+  @Override
+  @Test
+  public void testEnforceLeader() throws Exception {
+    super.testEnforceLeader();
+
+    MiniRaftClusterWithNetty.sendServerRequest.clear();
+    BlockRequestHandlingInjection.getInstance().unblockAll();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftExceptionWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftExceptionWithNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftExceptionWithNetty.java
new file mode 100644
index 0000000..3414a65
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftExceptionWithNetty.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.netty;
+
+import org.apache.ratis.RaftExceptionBaseTest;
+
+public class TestRaftExceptionWithNetty
+    extends RaftExceptionBaseTest<MiniRaftClusterWithNetty>
+    implements MiniRaftClusterWithNetty.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftReconfigurationWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftReconfigurationWithNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftReconfigurationWithNetty.java
new file mode 100644
index 0000000..15fa8e6
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftReconfigurationWithNetty.java
@@ -0,0 +1,31 @@
+/**
+ * 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.ratis.netty;
+
+import java.io.IOException;
+
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.server.impl.RaftReconfigurationBaseTest;
+
+public class TestRaftReconfigurationWithNetty
+    extends RaftReconfigurationBaseTest {
+  @Override
+  public MiniRaftCluster getCluster(int peerNum) throws IOException {
+    return MiniRaftClusterWithNetty.FACTORY.newCluster(peerNum, prop);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftSnapshotWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftSnapshotWithNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftSnapshotWithNetty.java
new file mode 100644
index 0000000..da861d5
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftSnapshotWithNetty.java
@@ -0,0 +1,31 @@
+/**
+ * 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.ratis.netty;
+
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.statemachine.RaftSnapshotBaseTest;
+
+import java.io.IOException;
+
+public class TestRaftSnapshotWithNetty extends RaftSnapshotBaseTest {
+  @Override
+  public MiniRaftCluster.Factory<?> getFactory() {
+    return MiniRaftClusterWithNetty.FACTORY;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftStateMachineExceptionWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftStateMachineExceptionWithNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftStateMachineExceptionWithNetty.java
new file mode 100644
index 0000000..34fddc9
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftStateMachineExceptionWithNetty.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.netty;
+
+import org.apache.ratis.server.impl.RaftStateMachineExceptionTests;
+
+public class TestRaftStateMachineExceptionWithNetty
+    extends RaftStateMachineExceptionTests<MiniRaftClusterWithNetty>
+    implements MiniRaftClusterWithNetty.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftWithNetty.java 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftWithNetty.java
new file mode 100644
index 0000000..28815d7
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/netty/TestRaftWithNetty.java
@@ -0,0 +1,34 @@
+/**
+ * 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.ratis.netty;
+
+import org.apache.ratis.RaftBasicTests;
+import org.apache.ratis.server.impl.BlockRequestHandlingInjection;
+import org.junit.Test;
+
+public class TestRaftWithNetty
+    extends RaftBasicTests<MiniRaftClusterWithNetty>
+    implements MiniRaftClusterWithNetty.FactoryGet {
+
+  @Override
+  @Test
+  public void testWithLoad() throws Exception {
+    super.testWithLoad();
+    BlockRequestHandlingInjection.getInstance().unblockAll();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestRetryCacheWithNettyRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestRetryCacheWithNettyRpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRetryCacheWithNettyRpc.java
new file mode 100644
index 0000000..659e426
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestRetryCacheWithNettyRpc.java
@@ -0,0 +1,45 @@
+/**
+ * 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.ratis.netty;
+
+import java.io.IOException;
+
+import org.apache.log4j.Level;
+import org.apache.ratis.RetryCacheTests;
+import org.apache.ratis.client.RaftClient;
+import org.apache.ratis.server.impl.RaftServerImpl;
+import org.apache.ratis.util.LogUtils;
+
+public class TestRetryCacheWithNettyRpc extends RetryCacheTests {
+  static {
+    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
+    LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
+  }
+
+  private final MiniRaftClusterWithNetty cluster;
+
+  public TestRetryCacheWithNettyRpc() throws IOException {
+    cluster = MiniRaftClusterWithNetty.FACTORY.newCluster(
+        NUM_SERVERS, getProperties());
+  }
+
+  @Override
+  public MiniRaftClusterWithNetty getCluster() {
+    return cluster;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestServerInformationWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestServerInformationWithNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestServerInformationWithNetty.java
new file mode 100644
index 0000000..d1bcae4
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestServerInformationWithNetty.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.netty;
+
+import org.apache.ratis.server.impl.ServerInformationBaseTest;
+
+public class TestServerInformationWithNetty
+    extends ServerInformationBaseTest<MiniRaftClusterWithNetty>
+    implements MiniRaftClusterWithNetty.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/netty/TestServerRestartWithNetty.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestServerRestartWithNetty.java
 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestServerRestartWithNetty.java
new file mode 100644
index 0000000..15dc688
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestServerRestartWithNetty.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.netty;
+
+import org.apache.ratis.server.ServerRestartTests;
+
+public class TestServerRestartWithNetty
+    extends ServerRestartTests<MiniRaftClusterWithNetty>
+    implements MiniRaftClusterWithNetty.FactoryGet {
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/protocol/TestRaftId.java
----------------------------------------------------------------------
diff --git a/ratis-test/src/test/java/org/apache/ratis/protocol/TestRaftId.java 
b/ratis-test/src/test/java/org/apache/ratis/protocol/TestRaftId.java
new file mode 100644
index 0000000..30b7ed5
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/protocol/TestRaftId.java
@@ -0,0 +1,54 @@
+/**
+ * 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.ratis.protocol;
+
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestRaftId extends BaseTest {
+  @Override
+  public int getGlobalTimeoutSeconds() {
+    return 1;
+  }
+
+  @Test
+  public void testClientId() {
+    final ClientId id = ClientId.randomId();
+    final ByteString bytes = id.toByteString();
+    Assert.assertEquals(bytes, id.toByteString());
+    Assert.assertEquals(id, ClientId.valueOf(bytes));
+  }
+
+  @Test
+  public void testRaftGroupId() {
+    final RaftGroupId id = RaftGroupId.randomId();
+    final ByteString bytes = id.toByteString();
+    Assert.assertEquals(bytes, id.toByteString());
+    Assert.assertEquals(id, RaftGroupId.valueOf(bytes));
+  }
+
+  @Test
+  public void testRaftPeerId() {
+    final RaftPeerId id = RaftPeerId.valueOf("abc");
+    final ByteString bytes = id.toByteString();
+    Assert.assertEquals(bytes, id.toByteString());
+    Assert.assertEquals(id, RaftPeerId.valueOf(bytes));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/TestRaftLogMetrics.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/TestRaftLogMetrics.java 
b/ratis-test/src/test/java/org/apache/ratis/server/TestRaftLogMetrics.java
new file mode 100644
index 0000000..58e319d
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/server/TestRaftLogMetrics.java
@@ -0,0 +1,119 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ratis.server;
+
+import com.codahale.metrics.Timer;
+import org.apache.log4j.Level;
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.RaftTestUtil;
+import org.apache.ratis.client.RaftClient;
+import org.apache.ratis.metrics.RatisMetricsRegistry;
+import org.apache.ratis.server.impl.RaftServerImpl;
+import org.apache.ratis.server.simulation.MiniRaftClusterWithSimulatedRpc;
+import org.apache.ratis.server.storage.RaftStorageTestUtils;
+import org.apache.ratis.statemachine.StateMachine;
+import org.apache.ratis.statemachine.impl.BaseStateMachine;
+import org.apache.ratis.util.JavaUtils;
+import org.apache.ratis.util.LogUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.management.ObjectName;
+import java.lang.management.ManagementFactory;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class TestRaftLogMetrics extends BaseTest
+    implements MiniRaftClusterWithSimulatedRpc.FactoryGet {
+
+  {
+    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
+  }
+
+  public static final int NUM_SERVERS = 3;
+
+  {
+    getProperties().setClass(MiniRaftCluster.STATEMACHINE_CLASS_KEY,
+        MetricsStateMachine.class, StateMachine.class);
+  }
+
+  static class MetricsStateMachine extends BaseStateMachine {
+    static MetricsStateMachine get(RaftServerImpl s) {
+      return (MetricsStateMachine)s.getStateMachine();
+    }
+
+    private final AtomicInteger flushCount = new AtomicInteger();
+
+    int getFlushCount() {
+      return flushCount.get();
+    }
+
+    @Override
+    public CompletableFuture<Void> flushStateMachineData(long index) {
+      flushCount.incrementAndGet();
+      return super.flushStateMachineData(index);
+    }
+  }
+
+  @Test
+  public void testFlushMetric() throws Exception {
+    try(final MiniRaftCluster cluster = newCluster(NUM_SERVERS)) {
+      cluster.start();
+      runTestFlushMetric(cluster);
+    }
+  }
+
+  static void runTestFlushMetric(MiniRaftCluster cluster) throws Exception {
+    int numMsg = 2;
+    final RaftTestUtil.SimpleMessage[] messages = 
RaftTestUtil.SimpleMessage.create(numMsg);
+
+    try (final RaftClient client = cluster.createClient()) {
+      for (RaftTestUtil.SimpleMessage message : messages) {
+        client.send(message);
+      }
+    }
+
+    // For leader, flush must happen before client can get replies.
+    assertFlushCount(cluster.getLeader());
+
+    // For followers, flush can be lagged behind.  Attempt multiple times.
+    for(RaftServerImpl f : cluster.getFollowers()) {
+      JavaUtils.attempt(() -> assertFlushCount(f), 10, 100, f.getId() + 
"-assertFlushCount", null);
+    }
+  }
+
+  static void assertFlushCount(RaftServerImpl server) throws Exception {
+      final String flushTimeMetric = 
RaftStorageTestUtils.getLogFlushTimeMetric(server.getId());
+      Timer tm = 
RatisMetricsRegistry.getRegistry().getTimers().get(flushTimeMetric);
+      Assert.assertNotNull(tm);
+
+      final MetricsStateMachine stateMachine = MetricsStateMachine.get(server);
+      final int expectedFlush = stateMachine.getFlushCount();
+
+      Assert.assertEquals(expectedFlush, tm.getCount());
+      Assert.assertTrue(tm.getMeanRate() > 0);
+
+      // Test jmx
+      ObjectName oname = new ObjectName("metrics", "name", flushTimeMetric);
+      Assert.assertEquals(expectedFlush,
+          ((Long) 
ManagementFactory.getPlatformMBeanServer().getAttribute(oname, "Count"))
+              .intValue());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/TestRaftServerConfigKeys.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/TestRaftServerConfigKeys.java
 
b/ratis-test/src/test/java/org/apache/ratis/server/TestRaftServerConfigKeys.java
new file mode 100644
index 0000000..53c8871
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/TestRaftServerConfigKeys.java
@@ -0,0 +1,98 @@
+/**
+ * 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.ratis.server;
+
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.util.FileUtils;
+import org.apache.ratis.util.JavaUtils;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * Test cases to verify RaftServerConfigKeys.
+ */
+public class TestRaftServerConfigKeys {
+
+  private static final Supplier<File> rootTestDir = JavaUtils.memoize(
+      () -> new File(BaseTest.getRootTestDir(),
+          TestRaftServerConfigKeys.class.getSimpleName() +
+              Integer.toHexString(ThreadLocalRandom.current().nextInt())));
+
+  @AfterClass
+  public static void tearDown() throws IOException {
+    FileUtils.deleteFully(rootTestDir.get());
+  }
+
+  /**
+   * Sets the value to <code>raft.server.storage.dir</code> via
+   * RaftServerConfigKeys and verifies it by reading directly from property.
+   */
+  @Test
+  public void testStorageDirsProperty() {
+    final File testDir = new File(
+        rootTestDir.get(), UUID.randomUUID().toString());
+    final List<File> directories = new ArrayList<>();
+    final  RaftProperties properties = new RaftProperties();
+
+    IntStream.range(0, 10).mapToObj((i) -> new File(testDir,
+        Integer.toString(i))).forEach(directories::add);
+    RaftServerConfigKeys.setStorageDirs(properties, directories);
+
+    final String expected = directories.stream().map(File::getAbsolutePath)
+        .collect(Collectors.joining(","));
+    final String actual = properties.get(RaftServerConfigKeys.STORAGE_DIR_KEY);
+    Assert.assertEquals(expected, actual);
+  }
+
+  /**
+   * Sets the value to <code>raft.server.storage.dir</code> via
+   * RaftServerConfigKeys and also verifies the same via RaftServerConfigKeys.
+   */
+  @Test
+  public void testStorageDirs() {
+    final File testDir = new File(
+        rootTestDir.get(), UUID.randomUUID().toString());
+    final List<File> directories = new ArrayList<>();
+    IntStream.range(0, 10).mapToObj((i) -> new File(testDir,
+        Integer.toString(i))).forEach(directories::add);
+    RaftProperties properties = new RaftProperties();
+    RaftServerConfigKeys.setStorageDirs(properties, directories);
+
+    final List<File> storageDirs = 
RaftServerConfigKeys.storageDirs(properties);
+    final List<String> expectedDirs = directories.stream()
+        .map(File::getAbsolutePath).collect(Collectors.toList());
+    final List<String> actualDirs = storageDirs.stream()
+        .map(File::getAbsolutePath).collect(Collectors.toList());
+    actualDirs.removeAll(expectedDirs);
+    Assert.assertEquals(directories.size(), storageDirs.size());
+    Assert.assertEquals(0, actualDirs.size());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java 
b/ratis-test/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
new file mode 100644
index 0000000..f060645
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
@@ -0,0 +1,109 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ratis.server.impl;
+
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.protocol.RaftGroupId;
+import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.server.RaftServerMXBean;
+import org.apache.ratis.server.simulation.MiniRaftClusterWithSimulatedRpc;
+import org.apache.ratis.util.JmxRegister;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import java.lang.management.ManagementFactory;
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.ratis.RaftTestUtil.waitForLeader;
+
+public class TestRaftServerJmx extends BaseTest {
+  @Test(timeout = 30000)
+  public void testJmxBeans() throws Exception {
+    final int NUM_SERVERS = 3;
+    final MiniRaftClusterWithSimulatedRpc cluster
+        = MiniRaftClusterWithSimulatedRpc.FACTORY.newCluster(3, new 
RaftProperties());
+    cluster.start();
+    waitForLeader(cluster);
+
+    MBeanServer platformMBeanServer = 
ManagementFactory.getPlatformMBeanServer();
+    Set<ObjectInstance> objectInstances = platformMBeanServer.queryMBeans(new 
ObjectName("Ratis:*"), null);
+    Assert.assertEquals(NUM_SERVERS, objectInstances.size());
+
+    for (ObjectInstance instance : objectInstances) {
+      Object groupId = 
platformMBeanServer.getAttribute(instance.getObjectName(), "GroupId");
+      Assert.assertEquals(cluster.getGroupId().toString(), groupId);
+    }
+    cluster.shutdown();
+  }
+
+  @Test(timeout = 30000)
+  public void testRegister() throws JMException {
+    {
+      final JmxRegister jmx = new JmxRegister();
+      runUnregister(false, jmx);
+
+      runRegister(true, "abc", jmx);
+      runRegister(false, "abc", jmx);
+      runUnregister(true, jmx);
+      runUnregister(false, jmx);
+
+      runRegister(true, "abc", jmx);
+      runUnregister(true, jmx);
+      runUnregister(false, jmx);
+    }
+
+    {
+      final JmxRegister jmx = new JmxRegister();
+      runRegister(true, "host:1234", jmx);
+      runUnregister(true, jmx);
+      runUnregister(false, jmx);
+    }
+  }
+
+  static void runRegister(boolean expectToSucceed, String name, JmxRegister 
jmx) {
+    final RaftServerMXBean mBean = new RaftServerMXBean() {
+      @Override
+      public String getId() { return null; }
+      @Override
+      public String getLeaderId() { return null; }
+      @Override
+      public long getCurrentTerm() { return 0; }
+      @Override
+      public String getGroupId() { return null; }
+      @Override
+      public String getRole() { return null; }
+      @Override
+      public List<String> getFollowers() { return null; }
+    };
+    final RaftPeerId id = RaftPeerId.valueOf(name);
+    final RaftGroupId groupId = RaftGroupId.randomId();
+    final boolean succeeded = RaftServerImpl.registerMBean(id, groupId, mBean, 
jmx);
+    Assert.assertEquals(expectToSucceed, succeeded);
+  }
+
+  static void runUnregister(boolean expectToSucceed, JmxRegister jmx) throws 
JMException {
+    final boolean succeeded = jmx.unregister();
+    Assert.assertEquals(expectToSucceed, succeeded);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/impl/TestServerState.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/impl/TestServerState.java 
b/ratis-test/src/test/java/org/apache/ratis/server/impl/TestServerState.java
new file mode 100644
index 0000000..5801d2b
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/server/impl/TestServerState.java
@@ -0,0 +1,127 @@
+/**
+ * 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.ratis.server.impl;
+
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.util.FileUtils;
+import org.apache.ratis.util.JavaUtils;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
+
+/**
+ * Test cases to verify ServerState.
+ */
+public class TestServerState {
+
+  private static final Supplier<File> rootTestDir = JavaUtils.memoize(
+      () -> new File(BaseTest.getRootTestDir(),
+          TestServerState.class.getSimpleName() +
+              Integer.toHexString(ThreadLocalRandom.current().nextInt())));
+
+  @AfterClass
+  public static void tearDown() throws IOException {
+    FileUtils.deleteFully(rootTestDir.get());
+  }
+
+  /**
+   * Tests choosing of storage directory when only one volume is configured.
+   *
+   * @throws IOException in case of exception.
+   */
+  @Test
+  public void testChooseStorageDirWithOneVolume() throws IOException {
+    File testDir = new File(rootTestDir.get(), UUID.randomUUID().toString());
+    List<File> directories = Collections.singletonList(testDir);
+    String subDirOne = UUID.randomUUID().toString();
+    String subDirTwo = UUID.randomUUID().toString();
+    File storageDirOne = ServerState.chooseStorageDir(directories, subDirOne);
+    File storageDirTwo = ServerState.chooseStorageDir(directories, subDirTwo);
+    File expectedOne = new File(testDir, subDirOne);
+    File expectedTwo = new File(testDir, subDirTwo);
+    Assert.assertEquals(expectedOne.getCanonicalPath(),
+        storageDirOne.getCanonicalPath());
+    Assert.assertEquals(expectedTwo.getCanonicalPath(),
+        storageDirTwo.getCanonicalPath());
+  }
+
+  /**
+   * Tests choosing of storage directory when multiple volumes are configured.
+   *
+   * @throws IOException in case of exception.
+   */
+  @Test
+  public void testChooseStorageDirWithMultipleVolumes() throws IOException {
+    File testDir = new File(rootTestDir.get(), UUID.randomUUID().toString());
+    List<File> directories = new ArrayList<>();
+    IntStream.range(0, 10).mapToObj((i) -> new File(testDir,
+        Integer.toString(i))).forEach((dir) -> {
+      try {
+        FileUtils.createDirectories(dir);
+        directories.add(dir);
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    });
+
+    directories.stream().filter((dir) -> Integer.parseInt(dir.getName()) != 6)
+        .forEach(
+            (dir) -> {
+              try {
+                FileUtils.createDirectories(
+                    new File(dir, UUID.randomUUID().toString()));
+              } catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            });
+    String subDir = UUID.randomUUID().toString();
+    File storageDirectory = ServerState.chooseStorageDir(directories, subDir);
+    File expected = new File(directories.get(6), subDir);
+    Assert.assertEquals(expected.getCanonicalPath(),
+        storageDirectory.getCanonicalPath());
+  }
+
+  /**
+   * Tests choosing of storage directory when only no volume is configured.
+   *
+   * @throws IOException in case of exception.
+   */
+  @Test
+  public void testChooseStorageDirWithNoVolume() {
+    try {
+      ServerState.chooseStorageDir(
+          Collections.emptyList(), UUID.randomUUID().toString());
+      Assert.fail();
+    } catch (IOException ex) {
+      String expectedErrMsg = "No storage directory found.";
+      Assert.assertEquals(expectedErrMsg, ex.getMessage());
+    }
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestGroupManagementWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestGroupManagementWithSimulatedRpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestGroupManagementWithSimulatedRpc.java
new file mode 100644
index 0000000..73fbae9
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestGroupManagementWithSimulatedRpc.java
@@ -0,0 +1,28 @@
+/**
+ * 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.ratis.server.simulation;
+
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.server.impl.GroupManagementBaseTest;
+
+public class TestGroupManagementWithSimulatedRpc extends 
GroupManagementBaseTest {
+  @Override
+  public MiniRaftCluster.Factory<? extends MiniRaftCluster> 
getClusterFactory() {
+    return MiniRaftClusterWithSimulatedRpc.FACTORY;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestLeaderElectionWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestLeaderElectionWithSimulatedRpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestLeaderElectionWithSimulatedRpc.java
new file mode 100644
index 0000000..7ea273b
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestLeaderElectionWithSimulatedRpc.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.server.simulation;
+
+import org.apache.ratis.server.impl.LeaderElectionTests;
+
+public class TestLeaderElectionWithSimulatedRpc
+    extends LeaderElectionTests<MiniRaftClusterWithSimulatedRpc>
+    implements MiniRaftClusterWithSimulatedRpc.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftExceptionWithSimulation.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftExceptionWithSimulation.java
 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftExceptionWithSimulation.java
new file mode 100644
index 0000000..f328ea3
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftExceptionWithSimulation.java
@@ -0,0 +1,25 @@
+/*
+ * 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.ratis.server.simulation;
+
+import org.apache.ratis.RaftExceptionBaseTest;
+
+public class TestRaftExceptionWithSimulation
+    extends RaftExceptionBaseTest<MiniRaftClusterWithSimulatedRpc>
+    implements MiniRaftClusterWithSimulatedRpc.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
new file mode 100644
index 0000000..b1bb26a
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftReconfigurationWithSimulatedRpc.java
@@ -0,0 +1,31 @@
+/**
+ * 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.ratis.server.simulation;
+
+import java.io.IOException;
+
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.server.impl.RaftReconfigurationBaseTest;
+
+public class TestRaftReconfigurationWithSimulatedRpc
+    extends RaftReconfigurationBaseTest {
+  @Override
+  public MiniRaftCluster getCluster(int peerNum) throws IOException {
+    return MiniRaftClusterWithSimulatedRpc.FACTORY.newCluster(peerNum, prop);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftSnapshotWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftSnapshotWithSimulatedRpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftSnapshotWithSimulatedRpc.java
new file mode 100644
index 0000000..8345872
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftSnapshotWithSimulatedRpc.java
@@ -0,0 +1,31 @@
+/**
+ * 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.ratis.server.simulation;
+
+import org.apache.ratis.MiniRaftCluster;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.statemachine.RaftSnapshotBaseTest;
+
+import java.io.IOException;
+
+public class TestRaftSnapshotWithSimulatedRpc extends RaftSnapshotBaseTest {
+  @Override
+  public MiniRaftCluster.Factory<?> getFactory() {
+    return MiniRaftClusterWithSimulatedRpc.FACTORY;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftStateMachineExceptionWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftStateMachineExceptionWithSimulatedRpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftStateMachineExceptionWithSimulatedRpc.java
new file mode 100644
index 0000000..d8ca86f
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftStateMachineExceptionWithSimulatedRpc.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.server.simulation;
+
+import org.apache.ratis.server.impl.RaftStateMachineExceptionTests;
+
+public class TestRaftStateMachineExceptionWithSimulatedRpc extends
+    RaftStateMachineExceptionTests<MiniRaftClusterWithSimulatedRpc>
+    implements MiniRaftClusterWithSimulatedRpc.FactoryGet {
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftWithSimulatedRpc.java
----------------------------------------------------------------------
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftWithSimulatedRpc.java
 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftWithSimulatedRpc.java
new file mode 100644
index 0000000..391a6fa
--- /dev/null
+++ 
b/ratis-test/src/test/java/org/apache/ratis/server/simulation/TestRaftWithSimulatedRpc.java
@@ -0,0 +1,25 @@
+/**
+ * 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.ratis.server.simulation;
+
+import org.apache.ratis.RaftBasicTests;
+
+public class TestRaftWithSimulatedRpc
+    extends RaftBasicTests<MiniRaftClusterWithSimulatedRpc>
+    implements MiniRaftClusterWithSimulatedRpc.FactoryGet {
+}


Reply via email to