You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by gr...@apache.org on 2018/10/17 20:43:07 UTC

[3/5] kudu git commit: KUDU-2411: (Part 1) Break out existing test utilities into a seperate module

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurity.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurity.java b/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurity.java
index 17ed3db..66f9e71 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurity.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurity.java
@@ -13,10 +13,10 @@
  */
 package org.apache.kudu.client;
 
-import static org.apache.kudu.util.AssertHelpers.assertEventuallyTrue;
-import static org.apache.kudu.util.ClientTestUtil.createBasicSchemaInsert;
-import static org.apache.kudu.util.ClientTestUtil.getBasicCreateTableOptions;
-import static org.apache.kudu.util.ClientTestUtil.getBasicSchema;
+import static org.apache.kudu.test.junit.AssertHelpers.assertEventuallyTrue;
+import static org.apache.kudu.test.ClientTestUtil.createBasicSchemaInsert;
+import static org.apache.kudu.test.ClientTestUtil.getBasicCreateTableOptions;
+import static org.apache.kudu.test.ClientTestUtil.getBasicSchema;
 import static org.junit.Assert.assertNotNull;
 
 import java.io.Closeable;
@@ -30,12 +30,14 @@ import java.util.concurrent.TimeUnit;
 import javax.security.auth.Subject;
 
 import org.apache.kudu.client.Client.AuthenticationCredentialsPB;
-import org.apache.kudu.client.MiniKuduCluster.MiniKuduClusterBuilder;
-import org.apache.kudu.junit.RetryRule;
+import org.apache.kudu.test.cluster.MiniKuduCluster;
+import org.apache.kudu.test.cluster.MiniKuduCluster.MiniKuduClusterBuilder;
+import org.apache.kudu.test.junit.RetryRule;
 import org.apache.kudu.master.Master.ConnectToMasterResponsePB;
-import org.apache.kudu.util.AssertHelpers;
-import org.apache.kudu.util.AssertHelpers.BooleanExpression;
-import org.apache.kudu.util.CapturingLogAppender;
+import org.apache.kudu.test.cluster.FakeDNS;
+import org.apache.kudu.test.junit.AssertHelpers;
+import org.apache.kudu.test.junit.AssertHelpers.BooleanExpression;
+import org.apache.kudu.test.CapturingLogAppender;
 import org.apache.kudu.util.SecurityUtil;
 import org.hamcrest.CoreMatchers;
 import org.junit.After;

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurityContextRealUser.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurityContextRealUser.java b/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurityContextRealUser.java
index a1aaa8e..d4dc753 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurityContextRealUser.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestSecurityContextRealUser.java
@@ -17,7 +17,7 @@
 
 package org.apache.kudu.client;
 
-import org.apache.kudu.client.MiniKuduCluster.MiniKuduClusterBuilder;
+import org.apache.kudu.test.cluster.MiniKuduCluster.MiniKuduClusterBuilder;
 import org.apache.kudu.test.KuduTestHarness;
 import org.junit.Before;
 import org.junit.Rule;
@@ -25,8 +25,8 @@ import org.junit.Test;
 
 import java.util.ArrayList;
 
-import static org.apache.kudu.util.ClientTestUtil.getBasicSchema;
-import static org.apache.kudu.util.ClientTestUtil.scanTableToStrings;
+import static org.apache.kudu.test.ClientTestUtil.getBasicSchema;
+import static org.apache.kudu.test.ClientTestUtil.scanTableToStrings;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/client/TestServerInfo.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/client/TestServerInfo.java b/java/kudu-client/src/test/java/org/apache/kudu/client/TestServerInfo.java
index 2d82272..233ae17 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestServerInfo.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestServerInfo.java
@@ -15,6 +15,7 @@ package org.apache.kudu.client;
 
 import java.net.InetAddress;
 
+import org.apache.kudu.test.cluster.FakeDNS;
 import org.junit.Assert;
 import org.junit.Test;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/client/TestStatistics.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/client/TestStatistics.java b/java/kudu-client/src/test/java/org/apache/kudu/client/TestStatistics.java
index f9cf0ca..1dfa319 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestStatistics.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestStatistics.java
@@ -16,9 +16,9 @@
 // under the License.
 package org.apache.kudu.client;
 
-import static org.apache.kudu.util.ClientTestUtil.createBasicSchemaInsert;
-import static org.apache.kudu.util.ClientTestUtil.getBasicCreateTableOptions;
-import static org.apache.kudu.util.ClientTestUtil.getBasicSchema;
+import static org.apache.kudu.test.ClientTestUtil.createBasicSchemaInsert;
+import static org.apache.kudu.test.ClientTestUtil.getBasicCreateTableOptions;
+import static org.apache.kudu.test.ClientTestUtil.getBasicSchema;
 import static org.junit.Assert.assertEquals;
 
 import org.apache.kudu.test.KuduTestHarness;

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/client/TestTimeouts.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/client/TestTimeouts.java b/java/kudu-client/src/test/java/org/apache/kudu/client/TestTimeouts.java
index ed8f56c..db3dcbd 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestTimeouts.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestTimeouts.java
@@ -16,9 +16,9 @@
 // under the License.
 package org.apache.kudu.client;
 
-import static org.apache.kudu.util.ClientTestUtil.createBasicSchemaInsert;
-import static org.apache.kudu.util.ClientTestUtil.getBasicCreateTableOptions;
-import static org.apache.kudu.util.ClientTestUtil.getBasicSchema;
+import static org.apache.kudu.test.ClientTestUtil.createBasicSchemaInsert;
+import static org.apache.kudu.test.ClientTestUtil.getBasicCreateTableOptions;
+import static org.apache.kudu.test.ClientTestUtil.getBasicSchema;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/junit/RetryRule.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/junit/RetryRule.java b/java/kudu-client/src/test/java/org/apache/kudu/junit/RetryRule.java
deleted file mode 100644
index fa0f6d0..0000000
--- a/java/kudu-client/src/test/java/org/apache/kudu/junit/RetryRule.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// 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.kudu.junit;
-
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.yetus.audience.InterfaceStability;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A JUnit rule to retry failed tests.
- * We use this with Gradle because it doesn't support
- * Surefire/Failsafe rerunFailingTestsCount like Maven does. We use the system
- * property rerunFailingTestsCount to mimic the maven arguments closely.
- */
-@InterfaceAudience.Private
-@InterfaceStability.Unstable
-public class RetryRule implements TestRule {
-
-  private static final Logger LOG = LoggerFactory.getLogger(RetryRule.class);
-  private static final int RETRY_COUNT = Integer.getInteger("rerunFailingTestsCount", 0);
-
-  public RetryRule () {}
-
-  @Override
-  public Statement apply(Statement base, Description description) {
-    return new RetryStatement(base, description, RETRY_COUNT);
-  }
-
-  private static class RetryStatement extends Statement {
-
-    private final Statement base;
-    private final Description description;
-    private final int retryCount;
-
-    RetryStatement(final Statement base, final Description description, final int retryCount) {
-      this.base = base;
-      this.description = description;
-      this.retryCount = retryCount;
-    }
-
-    @Override
-    public void evaluate() throws Throwable {
-      // If there are no retries, just pass through to evaluate as usual.
-      if (retryCount == 0) {
-        base.evaluate();
-        return;
-      }
-
-      // To retry we catch the exception for the evaluate, log a message, and retry.
-      // We track and throw the last failure if all tries fail.
-      Throwable lastException = null;
-      for (int i = 0; i < retryCount; i++) {
-        try {
-          base.evaluate();
-          return;
-        } catch (Throwable t) {
-          lastException = t;
-          LOG.error(description.getDisplayName() + ": failed run " + (i + 1), t);
-        }
-      }
-      LOG.error(description.getDisplayName() + ": giving up after " + retryCount + " failures");
-      throw lastException;
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/test/KuduTestHarness.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/test/KuduTestHarness.java b/java/kudu-client/src/test/java/org/apache/kudu/test/KuduTestHarness.java
deleted file mode 100644
index eeb9e9c..0000000
--- a/java/kudu-client/src/test/java/org/apache/kudu/test/KuduTestHarness.java
+++ /dev/null
@@ -1,445 +0,0 @@
-// 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.kudu.test;
-
-import com.google.common.base.Stopwatch;
-import com.stumbleupon.async.Deferred;
-import org.apache.kudu.Common;
-import org.apache.kudu.client.AsyncKuduClient;
-import org.apache.kudu.client.AsyncKuduClient.AsyncKuduClientBuilder;
-import org.apache.kudu.client.DeadlineTracker;
-import org.apache.kudu.client.FakeDNS;
-import org.apache.kudu.client.HostAndPort;
-import org.apache.kudu.client.KuduClient;
-import org.apache.kudu.client.KuduException;
-import org.apache.kudu.client.KuduTable;
-import org.apache.kudu.client.LocatedTablet;
-import org.apache.kudu.client.MiniKuduCluster;
-import org.apache.kudu.client.MiniKuduCluster.MiniKuduClusterBuilder;
-import org.apache.kudu.client.RemoteTablet;
-import org.apache.kudu.junit.RetryRule;
-import org.apache.kudu.master.Master;
-import org.apache.kudu.util.RandomUtils;
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.yetus.audience.InterfaceStability;
-import org.junit.rules.ExternalResource;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.fail;
-
-/**
- * A Junit Rule that manages a Kudu cluster and clients for testing.
- * This rule also includes utility methods for the cluster
- * and clients.
- *
- * <pre>
- * public static class TestFoo {
- *
- *  &#064;Rule
- *  public KuduTestHarness harness = new KuduTestHarness();
- *
- *  ...
- * }
- * </pre>
- */
-@InterfaceAudience.Private
-@InterfaceStability.Unstable
-public class KuduTestHarness extends ExternalResource {
-
-  private static final Logger LOG = LoggerFactory.getLogger(KuduTestHarness.class);
-
-  private static final int NUM_MASTER_SERVERS = 3;
-  private static final int NUM_TABLET_SERVERS = 3;
-
-  // Default timeout/sleep interval for various client operations,
-  // waiting for various jobs/threads to complete, etc.
-  public static final int DEFAULT_SLEEP = 50000;
-
-  private final Random randomForTSRestart = RandomUtils.getRandom();
-
-  private MiniKuduClusterBuilder clusterBuilder;
-  private MiniKuduCluster miniCluster;
-
-  // We create both versions of the asyncClient for ease of use.
-  private AsyncKuduClient asyncClient;
-  private KuduClient client;
-
-  public KuduTestHarness(final MiniKuduClusterBuilder clusterBuilder) {
-    this.clusterBuilder = clusterBuilder;
-  }
-
-  public KuduTestHarness() {
-    this.clusterBuilder = getBaseClusterBuilder();
-  }
-
-  /**
-   * Returns the base MiniKuduClusterBuilder used when creating a
-   * KuduTestHarness with the default constructor. This is useful
-   * if you want to add to the default cluster setup.
-   */
-  public static MiniKuduClusterBuilder getBaseClusterBuilder() {
-    return new MiniKuduClusterBuilder()
-        .numMasterServers(NUM_MASTER_SERVERS)
-        .numTabletServers(NUM_TABLET_SERVERS);
-  }
-
-  @Override
-  public Statement apply(Statement base, Description description) {
-    // Set any master server flags defined in the method level annotation.
-    MasterServerConfig masterServerConfig = description.getAnnotation(MasterServerConfig.class);
-    if (masterServerConfig != null) {
-      for (String flag : masterServerConfig.flags()) {
-        clusterBuilder.addMasterServerFlag(flag);
-      }
-    }
-    // Set any tablet server flags defined in the method level annotation.
-    TabletServerConfig tabletServerConfig = description.getAnnotation(TabletServerConfig.class);
-    if (tabletServerConfig != null) {
-      for (String flag : tabletServerConfig.flags()) {
-        clusterBuilder.addTabletServerFlag(flag);
-      }
-    }
-
-    // Generate the ExternalResource Statement.
-    Statement statement = super.apply(base, description);
-    // Wrap in the RetryRule to rerun flaky tests.
-    return new RetryRule().apply(statement, description);
-  }
-
-  @Override
-  public void before() throws Exception {
-    FakeDNS.getInstance().install();
-    LOG.info("Creating a new MiniKuduCluster...");
-    miniCluster = clusterBuilder.build();
-    LOG.info("Creating a new Kudu client...");
-    asyncClient = new AsyncKuduClientBuilder(miniCluster.getMasterAddressesAsString())
-        .defaultAdminOperationTimeoutMs(DEFAULT_SLEEP)
-        .build();
-    client = asyncClient.syncClient();
-  }
-
-  @Override
-  public void after() {
-    try {
-      if (client != null) {
-        client.shutdown();
-        // No need to explicitly shutdown the async client,
-        // shutting down the sync client effectively does that.
-      }
-    } catch (KuduException e) {
-      LOG.warn("Error while shutting down the test client");
-    } finally {
-      if (miniCluster != null) {
-        miniCluster.shutdown();
-      }
-    }
-  }
-
-  public KuduClient getClient() {
-    return client;
-  }
-
-  public AsyncKuduClient getAsyncClient() {
-    return asyncClient;
-  }
-
-  /**
-   * Helper method to easily kill a tablet server that serves the given table's only tablet's
-   * leader. The currently running test case will be failed if there's more than one tablet,
-   * if the tablet has no leader after some retries, or if the tablet server was already killed.
-   *
-   * This method is thread-safe.
-   * @param table a KuduTable which will get its single tablet's leader killed.
-   * @throws Exception
-   */
-  public void killTabletLeader(KuduTable table) throws Exception {
-    List<LocatedTablet> tablets = table.getTabletsLocations(DEFAULT_SLEEP);
-    if (tablets.isEmpty() || tablets.size() > 1) {
-      fail("Currently only support killing leaders for tables containing 1 tablet, table " +
-          table.getName() + " has " + tablets.size());
-    }
-    LocatedTablet tablet = tablets.get(0);
-    if (tablet.getReplicas().size() == 1) {
-      fail("Table " + table.getName() + " only has 1 tablet, please enable replication");
-    }
-
-    HostAndPort hp = findLeaderTabletServer(tablet);
-    miniCluster.killTabletServer(hp);
-  }
-
-  /**
-   * Helper method to kill a tablet server that serves the given tablet's
-   * leader. The currently running test case will be failed if the tablet has no
-   * leader after some retries, or if the tablet server was already killed.
-   *
-   * This method is thread-safe.
-   * @param tablet a RemoteTablet which will get its leader killed
-   * @throws Exception
-   */
-  public void killTabletLeader(RemoteTablet tablet) throws Exception {
-    killTabletLeader(new LocatedTablet(tablet));
-  }
-
-  /**
-   * Helper method to kill a tablet server that serves the given tablet's
-   * leader. The currently running test case will be failed if the tablet has no
-   * leader after some retries, or if the tablet server was already killed.
-   *
-   * This method is thread-safe.
-   * @param tablet a LocatedTablet which will get its leader killed
-   * @throws Exception
-   */
-  public void killTabletLeader(LocatedTablet tablet) throws Exception {
-    HostAndPort hp = findLeaderTabletServer(tablet);
-    miniCluster.killTabletServer(hp);
-  }
-
-  /**
-   * Finds the RPC port of the given tablet's leader tserver.
-   * @param tablet a LocatedTablet
-   * @return the host and port of the given tablet's leader tserver
-   * @throws Exception if we are unable to find the leader tserver
-   */
-  public HostAndPort findLeaderTabletServer(LocatedTablet tablet)
-      throws Exception {
-    LocatedTablet.Replica leader = null;
-    DeadlineTracker deadlineTracker = new DeadlineTracker();
-    deadlineTracker.setDeadline(DEFAULT_SLEEP);
-    while (leader == null) {
-      if (deadlineTracker.timedOut()) {
-        fail("Timed out while trying to find a leader for this table");
-      }
-
-      leader = tablet.getLeaderReplica();
-      if (leader == null) {
-        LOG.info("Sleeping while waiting for a tablet LEADER to arise, currently slept {} ms",
-            deadlineTracker.getElapsedMillis());
-        Thread.sleep(50);
-      }
-    }
-    return new HostAndPort(leader.getRpcHost(), leader.getRpcPort());
-  }
-
-  /**
-   * Helper method to easily kill the leader master.
-   *
-   * This method is thread-safe.
-   * @throws Exception if there is an error finding or killing the leader master.
-   */
-  public void killLeaderMasterServer() throws Exception {
-    HostAndPort hp = findLeaderMasterServer();
-    miniCluster.killMasterServer(hp);
-  }
-
-  /**
-   * Find the host and port of the leader master.
-   * @return the host and port of the leader master
-   * @throws Exception if we are unable to find the leader master
-   */
-  public HostAndPort findLeaderMasterServer() throws Exception {
-    Stopwatch sw = Stopwatch.createStarted();
-    while (sw.elapsed(TimeUnit.MILLISECONDS) < DEFAULT_SLEEP) {
-      Deferred<Master.GetTableLocationsResponsePB> masterLocD =
-          asyncClient.getMasterTableLocationsPB(null);
-      Master.GetTableLocationsResponsePB r = masterLocD.join(DEFAULT_SLEEP);
-      Common.HostPortPB pb = r.getTabletLocations(0)
-          .getReplicas(0)
-          .getTsInfo()
-          .getRpcAddresses(0);
-      if (pb.getPort() != -1) {
-        return new HostAndPort(pb.getHost(), pb.getPort());
-      }
-    }
-    throw new IOException(String.format("No leader master found after %d ms", DEFAULT_SLEEP));
-  }
-
-  /**
-   * Picks at random a tablet server that serves tablets from the passed table and restarts it.
-   * @param table table to query for a TS to restart
-   * @throws Exception
-   */
-  public void restartTabletServer(KuduTable table) throws Exception {
-    List<LocatedTablet> tablets = table.getTabletsLocations(DEFAULT_SLEEP);
-    if (tablets.isEmpty()) {
-      fail("Table " + table.getName() + " doesn't have any tablets");
-    }
-
-    LocatedTablet tablet = tablets.get(0);
-    LocatedTablet.Replica replica =
-        tablet.getReplicas().get(randomForTSRestart.nextInt(tablet.getReplicas().size()));
-    HostAndPort hp = new HostAndPort(replica.getRpcHost(), replica.getRpcPort());
-    miniCluster.killTabletServer(hp);
-    miniCluster.startTabletServer(hp);
-  }
-
-  /**
-   * Kills a tablet server that serves the given tablet's leader and restarts it.
-   * @param tablet a RemoteTablet which will get its leader killed and restarted
-   * @throws Exception
-   */
-  public void restartTabletServer(RemoteTablet tablet) throws Exception {
-    HostAndPort hp = findLeaderTabletServer(new LocatedTablet(tablet));
-    miniCluster.killTabletServer(hp);
-    miniCluster.startTabletServer(hp);
-  }
-
-  /**
-   * Kills and restarts the leader master.
-   * @throws Exception
-   */
-  public void restartLeaderMaster() throws Exception {
-    HostAndPort hp = findLeaderMasterServer();
-    miniCluster.killMasterServer(hp);
-    miniCluster.startMasterServer(hp);
-  }
-
-  /**
-   * Return the comma-separated list of "host:port" pairs that describes the master
-   * config for this cluster.
-   * @return The master config string.
-   */
-  public String getMasterAddressesAsString() {
-    return miniCluster.getMasterAddressesAsString();
-  }
-
-  /**
-   * @return the list of master servers
-   */
-  public List<HostAndPort> getMasterServers() {
-    return miniCluster.getMasterServers();
-  }
-
-  /**
-   * @return the list of tablet servers
-   */
-  public List<HostAndPort> getTabletServers() {
-    return miniCluster.getMasterServers();
-  }
-
-  /**
-   * @return path to the mini cluster root directory
-   */
-  public String getClusterRoot() {
-    return miniCluster.getClusterRoot();
-  }
-
-  /**
-   * Kills all the master servers.
-   * Does nothing to the servers that are already dead.
-   *
-   * @throws IOException
-   */
-  public void killAllMasterServers() throws IOException {
-    miniCluster.killAllMasterServers();
-  }
-
-  /**
-   * Starts all the master servers.
-   * Does nothing to the servers that are already running.
-   *
-   * @throws IOException
-   */
-  public void startAllMasterServers() throws IOException {
-    miniCluster.startAllMasterServers();
-  }
-
-  /**
-   * Kills all the tablet servers.
-   * Does nothing to the servers that are already dead.
-   *
-   * @throws IOException
-   */
-  public void killAllTabletServers() throws IOException {
-    miniCluster.killAllTabletServers();
-  }
-
-  /**
-   * Starts all the tablet servers.
-   * Does nothing to the servers that are already running.
-   *
-   * @throws IOException
-   */
-  public void startAllTabletServers() throws IOException {
-    miniCluster.startAllTabletServers();
-  }
-
-  /**
-   * Removes all credentials for all principals from the Kerberos credential cache.
-   */
-  public void kdestroy() throws IOException {
-    miniCluster.kdestroy();
-  }
-
-  /**
-   * Re-initialize Kerberos credentials for the given username, writing them
-   * into the Kerberos credential cache.
-   * @param username the username to kinit as
-   */
-  public void kinit(String username) throws IOException {
-    miniCluster.kinit(username);
-  }
-
-  /**
-   * Resets the clients so that their state is completely fresh, including meta
-   * cache, connections, open tables, sessions and scanners, and propagated timestamp.
-   */
-  public void resetClients() throws IOException {
-    client.shutdown();
-    asyncClient = new AsyncKuduClientBuilder(miniCluster.getMasterAddressesAsString())
-        .defaultAdminOperationTimeoutMs(DEFAULT_SLEEP)
-        .build();
-    client = asyncClient.syncClient();
-  }
-
-  /**
-   * An annotation that can be added to each test method to
-   * define additional master server flags to be used when
-   * creating the test cluster.
-   *
-   * ex: @MasterServerConfig(flags = { "key1=valA", "key2=valB" })
-   */
-  @Retention(RetentionPolicy.RUNTIME)
-  @Target({ElementType.METHOD})
-  public @interface MasterServerConfig {
-    String[] flags();
-  }
-
-  /**
-   * An annotation that can be added to each test method to
-   * define additional tablet server flags to be used when
-   * creating the test cluster.
-   *
-   * ex: @TabletServerConfig(flags = { "key1=valA", "key2=valB" })
-   */
-  @Retention(RetentionPolicy.RUNTIME)
-  @Target({ElementType.METHOD})
-  public @interface TabletServerConfig {
-    String[] flags();
-  }
-}

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/util/AssertHelpers.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/util/AssertHelpers.java b/java/kudu-client/src/test/java/org/apache/kudu/util/AssertHelpers.java
deleted file mode 100644
index ac30117..0000000
--- a/java/kudu-client/src/test/java/org/apache/kudu/util/AssertHelpers.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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.kudu.util;
-
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.yetus.audience.InterfaceStability;
-
-import static org.junit.Assert.assertTrue;
-
-@InterfaceAudience.Private
-@InterfaceStability.Unstable
-public class AssertHelpers {
-  public interface BooleanExpression {
-    boolean get() throws Exception;
-  }
-
-  // A looping check. It's mainly useful for scanners, since writes may take a little time to show
-  // up.
-  public static void assertEventuallyTrue(String description, BooleanExpression expression,
-                                          long timeoutMillis) throws Exception {
-    long deadlineNanos = System.nanoTime() + timeoutMillis * 1000000;
-    boolean success;
-
-    do {
-      success = expression.get();
-      if (success) break;
-      Thread.sleep(50); // Sleep for 50ms
-    } while (System.nanoTime() < deadlineNanos);
-
-    assertTrue(description, success);
-  }
-}

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/util/CapturingLogAppender.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/util/CapturingLogAppender.java b/java/kudu-client/src/test/java/org/apache/kudu/util/CapturingLogAppender.java
deleted file mode 100644
index c108826..0000000
--- a/java/kudu-client/src/test/java/org/apache/kudu/util/CapturingLogAppender.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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.kudu.util;
-
-import java.io.Closeable;
-import java.io.IOException;
-
-import com.google.common.base.Throwables;
-import org.apache.log4j.AppenderSkeleton;
-import org.apache.log4j.Layout;
-import org.apache.log4j.Logger;
-import org.apache.log4j.SimpleLayout;
-import org.apache.log4j.spi.LoggingEvent;
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.yetus.audience.InterfaceStability;
-
-/**
- * Test utility which wraps Log4j and captures all messages logged
- * while it is attached. This can be useful for asserting that a particular
- * message is (or is not) logged.
- */
-@InterfaceAudience.Private
-@InterfaceStability.Unstable
-public class CapturingLogAppender extends AppenderSkeleton {
-  private StringBuilder appended = new StringBuilder();
-  private static final Layout layout = new SimpleLayout();
-
-  @Override
-  public void close() {
-  }
-
-  @Override
-  public boolean requiresLayout() {
-    return false;
-  }
-
-  @Override
-  protected void append(LoggingEvent event) {
-    appended.append(layout.format(event));
-    if (event.getThrowableInformation() != null) {
-      appended.append(Throwables.getStackTraceAsString(
-          event.getThrowableInformation().getThrowable())).append("\n");
-    }
-  }
-
-  public String getAppendedText() {
-    return appended.toString();
-  }
-
-  /**
-   * Temporarily attach the capturing appender to the Log4j root logger.
-   * This can be used in a 'try-with-resources' block:
-   * <code>
-   *   try (Closeable c = capturer.attach()) {
-   *     ...
-   *   }
-   * </code>
-   */
-  public Closeable attach() {
-    Logger.getRootLogger().addAppender(this);
-    return new Closeable() {
-      @Override
-      public void close() throws IOException {
-        Logger.getRootLogger().removeAppender(CapturingLogAppender.this);
-      }
-    };
-  }
-}

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/util/ClientTestUtil.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/util/ClientTestUtil.java b/java/kudu-client/src/test/java/org/apache/kudu/util/ClientTestUtil.java
deleted file mode 100644
index 053e3e2..0000000
--- a/java/kudu-client/src/test/java/org/apache/kudu/util/ClientTestUtil.java
+++ /dev/null
@@ -1,375 +0,0 @@
-// 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.kudu.util;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-import com.stumbleupon.async.Callback;
-import com.stumbleupon.async.Deferred;
-import org.apache.kudu.ColumnSchema;
-import org.apache.kudu.ColumnTypeAttributes;
-import org.apache.kudu.Schema;
-import org.apache.kudu.Type;
-import org.apache.kudu.client.AsyncKuduClient;
-import org.apache.kudu.client.AsyncKuduScanner;
-import org.apache.kudu.client.AsyncKuduSession;
-import org.apache.kudu.client.CreateTableOptions;
-import org.apache.kudu.client.Insert;
-import org.apache.kudu.client.KuduClient;
-import org.apache.kudu.client.KuduException;
-import org.apache.kudu.client.KuduPredicate;
-import org.apache.kudu.client.KuduScanToken;
-import org.apache.kudu.client.KuduScanner;
-import org.apache.kudu.client.KuduSession;
-import org.apache.kudu.client.KuduTable;
-import org.apache.kudu.client.PartialRow;
-import org.apache.kudu.client.RowResult;
-import org.apache.kudu.client.RowResultIterator;
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.yetus.audience.InterfaceStability;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Utilities useful for cluster testing.
- */
-@InterfaceAudience.Private
-@InterfaceStability.Unstable
-public abstract class ClientTestUtil {
-
-  private static final Logger LOG = LoggerFactory.getLogger(ClientTestUtil.class);
-
-  public static final Callback<Object, Object> defaultErrorCB = new Callback<Object, Object>() {
-    @Override
-    public Object call(Object arg) throws Exception {
-      if (arg == null) {
-        return null;
-      }
-      if (arg instanceof Exception) {
-        LOG.warn("Got exception", (Exception) arg);
-      } else {
-        LOG.warn("Got an error response back {}", arg);
-      }
-      return new Exception("cannot recover from error: " + arg);
-    }
-  };
-
-  /**
-   * Counts the rows from the {@code scanner} until exhaustion. It doesn't require the scanner to
-   * be new, so it can be used to finish scanning a previously-started scan.
-   */
-  public static int countRowsInScan(AsyncKuduScanner scanner, long timeoutMs) throws Exception {
-    final AtomicInteger counter = new AtomicInteger();
-
-    Callback<Object, RowResultIterator> cb = new Callback<Object, RowResultIterator>() {
-      @Override
-      public Object call(RowResultIterator arg) throws Exception {
-        if (arg == null) return null;
-        counter.addAndGet(arg.getNumRows());
-        return null;
-      }
-    };
-
-    while (scanner.hasMoreRows()) {
-      Deferred<RowResultIterator> data = scanner.nextRows();
-      data.addCallbacks(cb, defaultErrorCB);
-      data.join(timeoutMs);
-    }
-    return counter.get();
-  }
-
-  /**
-   * Same as {@link #countRowsInScan(AsyncKuduScanner, long)}, but defaults the timeout to 60
-   * seconds.
-   */
-  public static int countRowsInScan(AsyncKuduScanner scanner) throws Exception {
-    return countRowsInScan(scanner, 60000);
-  }
-
-  public static int countRowsInScan(KuduScanner scanner) throws KuduException {
-    int counter = 0;
-    while (scanner.hasMoreRows()) {
-      counter += scanner.nextRows().getNumRows();
-    }
-    return counter;
-  }
-
-  /**
-   * Scans the table and returns the number of rows.
-   * @param table the table
-   * @param predicates optional predicates to apply to the scan
-   * @return the number of rows in the table matching the predicates
-   */
-  public static long countRowsInTable(KuduTable table, KuduPredicate... predicates) throws KuduException {
-    KuduScanner.KuduScannerBuilder scanBuilder =
-        table.getAsyncClient().syncClient().newScannerBuilder(table);
-    for (KuduPredicate predicate : predicates) {
-      scanBuilder.addPredicate(predicate);
-    }
-    scanBuilder.setProjectedColumnIndexes(ImmutableList.<Integer>of());
-    return countRowsInScan(scanBuilder.build());
-  }
-
-  /**
-   * Counts the rows in the provided scan tokens.
-   */
-  public static int countScanTokenRows(List<KuduScanToken> tokens, final String masterAddresses,
-                                       final long operationTimeoutMs)
-      throws IOException, InterruptedException {
-    final AtomicInteger count = new AtomicInteger(0);
-    List<Thread> threads = new ArrayList<>();
-    for (final KuduScanToken token : tokens) {
-      final byte[] serializedToken = token.serialize();
-      Thread thread = new Thread(new Runnable() {
-        @Override
-        public void run() {
-          try (KuduClient contextClient = new KuduClient.KuduClientBuilder(masterAddresses)
-                   .defaultAdminOperationTimeoutMs(operationTimeoutMs)
-                   .build()) {
-            KuduScanner scanner = KuduScanToken.deserializeIntoScanner(serializedToken, contextClient);
-            try {
-              int localCount = 0;
-              while (scanner.hasMoreRows()) {
-                localCount += Iterators.size(scanner.nextRows());
-              }
-              count.addAndGet(localCount);
-            } finally {
-              scanner.close();
-            }
-          } catch (Exception e) {
-            LOG.error("exception in parallel token scanner", e);
-          }
-        }
-      });
-      thread.run();
-      threads.add(thread);
-    }
-
-    for (Thread thread : threads) {
-      thread.join();
-    }
-    return count.get();
-  }
-
-  public static List<String> scanTableToStrings(KuduTable table,
-                                                KuduPredicate... predicates) throws Exception {
-    List<String> rowStrings = Lists.newArrayList();
-    KuduScanner.KuduScannerBuilder scanBuilder =
-        table.getAsyncClient().syncClient().newScannerBuilder(table);
-    for (KuduPredicate predicate : predicates) {
-      scanBuilder.addPredicate(predicate);
-    }
-    KuduScanner scanner = scanBuilder.build();
-    while (scanner.hasMoreRows()) {
-      RowResultIterator rows = scanner.nextRows();
-      for (RowResult r : rows) {
-        rowStrings.add(r.rowToString());
-      }
-    }
-    Collections.sort(rowStrings);
-    return rowStrings;
-  }
-
-  public static Schema getSchemaWithAllTypes() {
-    List<ColumnSchema> columns =
-        ImmutableList.of(
-            new ColumnSchema.ColumnSchemaBuilder("int8", Type.INT8).key(true).build(),
-            new ColumnSchema.ColumnSchemaBuilder("int16", Type.INT16).build(),
-            new ColumnSchema.ColumnSchemaBuilder("int32", Type.INT32).build(),
-            new ColumnSchema.ColumnSchemaBuilder("int64", Type.INT64).build(),
-            new ColumnSchema.ColumnSchemaBuilder("bool", Type.BOOL).build(),
-            new ColumnSchema.ColumnSchemaBuilder("float", Type.FLOAT).build(),
-            new ColumnSchema.ColumnSchemaBuilder("double", Type.DOUBLE).build(),
-            new ColumnSchema.ColumnSchemaBuilder("string", Type.STRING).build(),
-            new ColumnSchema.ColumnSchemaBuilder("binary-array", Type.BINARY).build(),
-            new ColumnSchema.ColumnSchemaBuilder("binary-bytebuffer", Type.BINARY).build(),
-            new ColumnSchema.ColumnSchemaBuilder("null", Type.STRING).nullable(true).build(),
-            new ColumnSchema.ColumnSchemaBuilder("timestamp", Type.UNIXTIME_MICROS).build(),
-            new ColumnSchema.ColumnSchemaBuilder("decimal", Type.DECIMAL)
-                .typeAttributes(DecimalUtil.typeAttributes(5, 3)).build());
-
-    return new Schema(columns);
-  }
-
-  public static CreateTableOptions getAllTypesCreateTableOptions() {
-    return new CreateTableOptions().setRangePartitionColumns(ImmutableList.of("int8"));
-  }
-
-  public static Schema getBasicSchema() {
-    ArrayList<ColumnSchema> columns = new ArrayList<>(5);
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("key", Type.INT32).key(true).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("column1_i", Type.INT32).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("column2_i", Type.INT32).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("column3_s", Type.STRING)
-                    .nullable(true)
-                    .desiredBlockSize(4096)
-                    .encoding(ColumnSchema.Encoding.DICT_ENCODING)
-                    .compressionAlgorithm(ColumnSchema.CompressionAlgorithm.LZ4)
-                    .build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("column4_b", Type.BOOL).build());
-    return new Schema(columns);
-  }
-
-  public static CreateTableOptions getBasicCreateTableOptions() {
-    return new CreateTableOptions().setRangePartitionColumns(ImmutableList.of("key"));
-  }
-
-  /**
-   * Creates table options with non-covering range partitioning for a table with
-   * the basic schema. Range partition key ranges fall between the following values:
-   *
-   * [  0,  50)
-   * [ 50, 100)
-   * [200, 300)
-   */
-  public static CreateTableOptions getBasicTableOptionsWithNonCoveredRange() {
-    Schema schema = getBasicSchema();
-    CreateTableOptions option = new CreateTableOptions();
-    option.setRangePartitionColumns(ImmutableList.of("key"));
-
-    PartialRow aLowerBound = schema.newPartialRow();
-    aLowerBound.addInt("key", 0);
-    PartialRow aUpperBound = schema.newPartialRow();
-    aUpperBound.addInt("key", 100);
-    option.addRangePartition(aLowerBound, aUpperBound);
-
-    PartialRow bLowerBound = schema.newPartialRow();
-    bLowerBound.addInt("key", 200);
-    PartialRow bUpperBound = schema.newPartialRow();
-    bUpperBound.addInt("key", 300);
-    option.addRangePartition(bLowerBound, bUpperBound);
-
-    PartialRow split = schema.newPartialRow();
-    split.addInt("key", 50);
-    option.addSplitRow(split);
-    return option;
-  }
-
-  /**
-   * A generic helper function to create a table with default test options.
-   */
-  public static KuduTable createDefaultTable(KuduClient client, String tableName) throws KuduException {
-    return client.createTable(tableName, getBasicSchema(), getBasicCreateTableOptions());
-  }
-
-  /**
-   * Load a table of default schema with the specified number of records, in ascending key order.
-   */
-  public static void loadDefaultTable(KuduClient client, String tableName, int numRows)
-      throws KuduException {
-    KuduTable table = client.openTable(tableName);
-    KuduSession session = client.newSession();
-    for (int i = 0; i < numRows; i++) {
-      Insert insert = createBasicSchemaInsert(table, i);
-      session.apply(insert);
-    }
-    session.flush();
-    session.close();
-  }
-
-  public static Insert createBasicSchemaInsert(KuduTable table, int key) {
-    Insert insert = table.newInsert();
-    PartialRow row = insert.getRow();
-    row.addInt(0, key);
-    row.addInt(1, 2);
-    row.addInt(2, 3);
-    row.addString(3, "a string");
-    row.addBoolean(4, true);
-    return insert;
-  }
-
-  public static KuduTable createFourTabletsTableWithNineRows(AsyncKuduClient client,
-                                                             String tableName,
-                                                             final long timeoutMs)
-      throws Exception {
-    final int[] KEYS = new int[] { 10, 20, 30 };
-    final Schema basicSchema = getBasicSchema();
-    CreateTableOptions builder = getBasicCreateTableOptions();
-    for (int i : KEYS) {
-      PartialRow splitRow = basicSchema.newPartialRow();
-      splitRow.addInt(0, i);
-      builder.addSplitRow(splitRow);
-    }
-    KuduTable table = client.syncClient().createTable(tableName, basicSchema, builder);
-    AsyncKuduSession session = client.newSession();
-
-    // create a table with on empty tablet and 3 tablets of 3 rows each
-    for (int key1 : KEYS) {
-      for (int key2 = 1; key2 <= 3; key2++) {
-        Insert insert = table.newInsert();
-        PartialRow row = insert.getRow();
-        row.addInt(0, key1 + key2);
-        row.addInt(1, key1);
-        row.addInt(2, key2);
-        row.addString(3, "a string");
-        row.addBoolean(4, true);
-        session.apply(insert).join(timeoutMs);
-      }
-    }
-    session.close().join(timeoutMs);
-    return table;
-  }
-
-  public static Schema createManyStringsSchema() {
-    ArrayList<ColumnSchema> columns = new ArrayList<ColumnSchema>(4);
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("key", Type.STRING).key(true).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c1", Type.STRING).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c2", Type.STRING).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c3", Type.STRING).nullable(true).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c4", Type.STRING).nullable(true).build());
-    return new Schema(columns);
-  }
-
-  public static Schema createSchemaWithBinaryColumns() {
-    ArrayList<ColumnSchema> columns = new ArrayList<ColumnSchema>();
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("key", Type.BINARY).key(true).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c1", Type.STRING).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c2", Type.DOUBLE).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c3", Type.BINARY).nullable(true).build());
-    return new Schema(columns);
-  }
-
-  public static Schema createSchemaWithTimestampColumns() {
-    ArrayList<ColumnSchema> columns = new ArrayList<ColumnSchema>();
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("key", Type.UNIXTIME_MICROS).key(true).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c1", Type.UNIXTIME_MICROS).nullable(true).build());
-    return new Schema(columns);
-  }
-
-  public static Schema createSchemaWithDecimalColumns() {
-    ArrayList<ColumnSchema> columns = new ArrayList<ColumnSchema>();
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("key", Type.DECIMAL).key(true)
-        .typeAttributes(
-            new ColumnTypeAttributes.ColumnTypeAttributesBuilder()
-                .precision(DecimalUtil.MAX_DECIMAL64_PRECISION).build()
-        ).build());
-    columns.add(new ColumnSchema.ColumnSchemaBuilder("c1", Type.DECIMAL).nullable(true)
-        .typeAttributes(
-            new ColumnTypeAttributes.ColumnTypeAttributesBuilder()
-                .precision(DecimalUtil.MAX_DECIMAL128_PRECISION).build()
-        ).build());
-    return new Schema(columns);
-  }
-}

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/util/KuduBinaryLocator.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/util/KuduBinaryLocator.java b/java/kudu-client/src/test/java/org/apache/kudu/util/KuduBinaryLocator.java
deleted file mode 100644
index 1b11d61..0000000
--- a/java/kudu-client/src/test/java/org/apache/kudu/util/KuduBinaryLocator.java
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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.kudu.util;
-
-import com.google.common.io.CharStreams;
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.yetus.audience.InterfaceStability;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-@InterfaceAudience.Private
-@InterfaceStability.Unstable
-public class KuduBinaryLocator {
-  private static final Logger LOG = LoggerFactory.getLogger(KuduBinaryLocator.class);
-
-  private static final String KUDU_BIN_DIR_PROP = "kuduBinDir";
-
-  /**
-   * Find the binary directory within the build tree.
-   *
-   * Uses the following priority:
-   *    - If kuduBinDir system property is set, use that.
-   *    - If the `kudu` binary is found on the PATH using `which kudu`,
-   *      use its parent directory.
-   */
-  private static String findBinaryDir() {
-    // If kuduBinDir system property is set, use that.
-    String kuduBinDirProp = System.getProperty(KUDU_BIN_DIR_PROP);
-    if (kuduBinDirProp != null) {
-      LOG.info("Using Kudu binary directory specified by system property '{}': {}",
-          KUDU_BIN_DIR_PROP, kuduBinDirProp);
-      return kuduBinDirProp;
-    }
-
-    // If the `kudu` binary is found on the PATH using `which kudu`, use its parent directory.
-    try {
-      Runtime runtime = Runtime.getRuntime();
-      Process process = runtime.exec("which kudu");
-      int errorCode = process.waitFor();
-      if (errorCode == 0) {
-        try(Reader reader = new InputStreamReader(process.getInputStream(), UTF_8)) {
-          String kuduBinary = CharStreams.toString(reader);
-          String kuduBinDir = new File(kuduBinary).getParent();
-          LOG.info("Using Kudu binary directory found on path with 'which kudu': {}", kuduBinDir);
-          return kuduBinDir;
-        }
-      }
-    } catch (IOException | InterruptedException ex) {
-      throw new RuntimeException("Error while locating kudu binary", ex);
-    }
-
-    throw new RuntimeException("Could not locate the kudu binary directory. " +
-        "Set the system variable " + KUDU_BIN_DIR_PROP +
-        " or ensure the `kudu` binary is on your path.");
-  }
-
-  /**
-   * @param binName the binary to look for (eg 'kudu-tserver')
-   * @return the absolute path of that binary
-   * @throws FileNotFoundException if no such binary is found
-   */
-  public static String findBinary(String binName) throws FileNotFoundException {
-    String binDir = findBinaryDir();
-
-    File candidate = new File(binDir, binName);
-    if (candidate.canExecute()) {
-      return candidate.getAbsolutePath();
-    }
-    throw new FileNotFoundException("Cannot find binary " + binName +
-        " in binary directory " + binDir);
-  }
-}

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/util/ProtobufUtils.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/util/ProtobufUtils.java b/java/kudu-client/src/test/java/org/apache/kudu/util/ProtobufUtils.java
deleted file mode 100644
index 70ae846..0000000
--- a/java/kudu-client/src/test/java/org/apache/kudu/util/ProtobufUtils.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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.kudu.util;
-
-import com.google.protobuf.ByteString;
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.yetus.audience.InterfaceStability;
-
-import org.apache.kudu.Common;
-import org.apache.kudu.consensus.Metadata;
-import org.apache.kudu.master.Master;
-
-@InterfaceAudience.Private
-@InterfaceStability.Unstable
-public class ProtobufUtils {
-
-  /**
-   * Get a PartitionPB with empty start and end keys.
-   * @return a fake partition
-   */
-  public static Common.PartitionPB.Builder getFakePartitionPB() {
-    Common.PartitionPB.Builder partition = Common.PartitionPB.newBuilder();
-    partition.setPartitionKeyStart(ByteString.EMPTY);
-    partition.setPartitionKeyEnd(ByteString.EMPTY);
-    return partition;
-  }
-
-  /**
-   * Create a ReplicaPB based on the passed information.
-   * @param uuid server's identifier
-   * @param host server's hostname
-   * @param port server's port
-   * @param role server's role in the configuration
-   * @return a fake ReplicaPB
-   */
-  public static Master.TabletLocationsPB.ReplicaPB.Builder getFakeTabletReplicaPB(
-      String uuid, String host, int port, Metadata.RaftPeerPB.Role role) {
-    Master.TSInfoPB.Builder tsInfoBuilder = Master.TSInfoPB.newBuilder();
-    Common.HostPortPB.Builder hostBuilder = Common.HostPortPB.newBuilder();
-    hostBuilder.setHost(host);
-    hostBuilder.setPort(port);
-    tsInfoBuilder.addRpcAddresses(hostBuilder);
-    tsInfoBuilder.setPermanentUuid(ByteString.copyFromUtf8(uuid));
-    Master.TabletLocationsPB.ReplicaPB.Builder replicaBuilder =
-        Master.TabletLocationsPB.ReplicaPB.newBuilder();
-    replicaBuilder.setTsInfo(tsInfoBuilder);
-    replicaBuilder.setRole(role);
-    return replicaBuilder;
-  }
-}

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-client/src/test/java/org/apache/kudu/util/RandomUtils.java
----------------------------------------------------------------------
diff --git a/java/kudu-client/src/test/java/org/apache/kudu/util/RandomUtils.java b/java/kudu-client/src/test/java/org/apache/kudu/util/RandomUtils.java
deleted file mode 100644
index 2b45464..0000000
--- a/java/kudu-client/src/test/java/org/apache/kudu/util/RandomUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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.kudu.util;
-
-import org.apache.yetus.audience.InterfaceAudience;
-import org.apache.yetus.audience.InterfaceStability;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Random;
-
-@InterfaceAudience.Private
-@InterfaceStability.Unstable
-public class RandomUtils {
-  private static final Logger LOG = LoggerFactory.getLogger(RandomUtils.class);
-
-  private static final String TEST_RANDOM_SEED_PROP = "testRandomSeed";
-
-  /**
-   * Get an instance of Random for use in tests and logs the seed used.
-   *
-   * Uses a default seed of System.currentTimeMillis() with the option to
-   * override via the testRandomSeed system property.
-   */
-  public static Random getRandom() {
-    // First check the system property.
-    long seed = System.currentTimeMillis();
-    if (System.getProperty(TEST_RANDOM_SEED_PROP) != null) {
-      seed = Long.parseLong(System.getProperty(TEST_RANDOM_SEED_PROP));
-      LOG.info("System property {} is defined. Overriding random seed.", TEST_RANDOM_SEED_PROP, seed);
-    }
-    LOG.info("Using random seed: {}", seed);
-    return new Random(seed);
-  }
-}

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-flume-sink/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/build.gradle b/java/kudu-flume-sink/build.gradle
index e09f0ae..9e569fd 100644
--- a/java/kudu-flume-sink/build.gradle
+++ b/java/kudu-flume-sink/build.gradle
@@ -31,7 +31,7 @@ dependencies {
   provided libs.hadoopCommon
   provided libs.slf4jApi
 
-  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile project(path: ":kudu-test-utils", configuration: "shadow")
   testCompile libs.junit
 }
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducerTest.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducerTest.java b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducerTest.java
index 91bc339..5517f1a 100644
--- a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducerTest.java
+++ b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/AvroKuduOperationsProducerTest.java
@@ -25,7 +25,7 @@ import static org.apache.kudu.flume.sink.AvroKuduOperationsProducer.SCHEMA_PROP;
 import static org.apache.kudu.flume.sink.AvroKuduOperationsProducer.SCHEMA_URL_HEADER;
 import static org.apache.kudu.flume.sink.KuduSinkConfigurationConstants.PRODUCER;
 import static org.apache.kudu.flume.sink.KuduSinkConfigurationConstants.PRODUCER_PREFIX;
-import static org.apache.kudu.util.ClientTestUtil.scanTableToStrings;
+import static org.apache.kudu.test.ClientTestUtil.scanTableToStrings;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KeyedKuduOperationsProducerTest.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KeyedKuduOperationsProducerTest.java b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KeyedKuduOperationsProducerTest.java
index 8914b06..58200fc 100644
--- a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KeyedKuduOperationsProducerTest.java
+++ b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KeyedKuduOperationsProducerTest.java
@@ -24,7 +24,7 @@ import static org.apache.kudu.flume.sink.KuduSinkConfigurationConstants.PRODUCER
 import static org.apache.kudu.flume.sink.SimpleKeyedKuduOperationsProducer.KEY_COLUMN_DEFAULT;
 import static org.apache.kudu.flume.sink.SimpleKeyedKuduOperationsProducer.OPERATION_PROP;
 import static org.apache.kudu.flume.sink.SimpleKeyedKuduOperationsProducer.PAYLOAD_COLUMN_DEFAULT;
-import static org.apache.kudu.util.ClientTestUtil.scanTableToStrings;
+import static org.apache.kudu.test.ClientTestUtil.scanTableToStrings;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KuduSinkTest.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KuduSinkTest.java b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KuduSinkTest.java
index 3887482..89b8c1a 100644
--- a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KuduSinkTest.java
+++ b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/KuduSinkTest.java
@@ -19,7 +19,7 @@
 package org.apache.kudu.flume.sink;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.kudu.util.ClientTestUtil.scanTableToStrings;
+import static org.apache.kudu.test.ClientTestUtil.scanTableToStrings;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerParseErrorTest.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerParseErrorTest.java b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerParseErrorTest.java
index b2bf746..2db41e0 100644
--- a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerParseErrorTest.java
+++ b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerParseErrorTest.java
@@ -17,7 +17,6 @@
 
 package org.apache.kudu.flume.sink;
 
-
 import static org.apache.kudu.flume.sink.RegexpKuduOperationsProducer.BAD_COLUMN_VALUE_POLICY_PROP;
 import static org.apache.kudu.flume.sink.RegexpKuduOperationsProducer.MISSING_COLUMN_POLICY_PROP;
 import static org.apache.kudu.flume.sink.RegexpKuduOperationsProducer.OPERATION_PROP;
@@ -47,7 +46,7 @@ import org.apache.kudu.Schema;
 import org.apache.kudu.Type;
 import org.apache.kudu.client.CreateTableOptions;
 import org.apache.kudu.client.KuduTable;
-import org.apache.kudu.util.CapturingLogAppender;
+import org.apache.kudu.test.CapturingLogAppender;
 
 public class RegexpKuduOperationsProducerParseErrorTest {
   private static final String TEST_REGEXP = "(?<key>\\d+),(?<byteFld>\\d+),(?<stringFld>\\w+)";

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerTest.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerTest.java b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerTest.java
index b5c4e28..f063349 100644
--- a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerTest.java
+++ b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/RegexpKuduOperationsProducerTest.java
@@ -23,7 +23,7 @@ import static org.apache.kudu.flume.sink.KuduSinkConfigurationConstants.PRODUCER
 import static org.apache.kudu.flume.sink.KuduSinkConfigurationConstants.PRODUCER_PREFIX;
 import static org.apache.kudu.flume.sink.RegexpKuduOperationsProducer.OPERATION_PROP;
 import static org.apache.kudu.flume.sink.RegexpKuduOperationsProducer.PATTERN_PROP;
-import static org.apache.kudu.util.ClientTestUtil.scanTableToStrings;
+import static org.apache.kudu.test.ClientTestUtil.scanTableToStrings;
 import static org.junit.Assert.assertEquals;
 
 import java.util.ArrayList;

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/SecureKuduSinkTest.java
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/SecureKuduSinkTest.java b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/SecureKuduSinkTest.java
index e84e1f9..187cf68 100644
--- a/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/SecureKuduSinkTest.java
+++ b/java/kudu-flume-sink/src/test/java/org/apache/kudu/flume/sink/SecureKuduSinkTest.java
@@ -20,7 +20,7 @@
 package org.apache.kudu.flume.sink;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.kudu.util.ClientTestUtil.scanTableToStrings;
+import static org.apache.kudu.test.ClientTestUtil.scanTableToStrings;
 import static org.apache.kudu.util.SecurityUtil.KUDU_TICKETCACHE_PROPERTY;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -45,7 +45,7 @@ import org.apache.kudu.Schema;
 import org.apache.kudu.Type;
 import org.apache.kudu.client.CreateTableOptions;
 import org.apache.kudu.client.KuduTable;
-import org.apache.kudu.client.MiniKuduCluster.MiniKuduClusterBuilder;
+import org.apache.kudu.test.cluster.MiniKuduCluster.MiniKuduClusterBuilder;
 
 public class SecureKuduSinkTest {
   private static final Logger LOG = LoggerFactory.getLogger(SecureKuduSinkTest.class);

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-hive/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-hive/build.gradle b/java/kudu-hive/build.gradle
index 56d0fbc..3d4bb2b 100644
--- a/java/kudu-hive/build.gradle
+++ b/java/kudu-hive/build.gradle
@@ -26,7 +26,7 @@ dependencies {
   provided libs.hadoopCommon
   provided libs.hadoopMRClientCommon
 
-  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile project(path: ":kudu-test-utils", configuration: "shadow")
   testCompile libs.hiveMetastoreTest
   testCompile libs.junit
   testCompile libs.log4j

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java
----------------------------------------------------------------------
diff --git a/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java b/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java
index 0634aa2..6bafa4a 100644
--- a/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java
+++ b/java/kudu-hive/src/test/java/org/apache/kudu/hive/metastore/TestKuduMetastorePlugin.java
@@ -39,7 +39,7 @@ import org.apache.hadoop.hive.metastore.api.SerDeInfo;
 import org.apache.hadoop.hive.metastore.api.StorageDescriptor;
 import org.apache.hadoop.hive.metastore.api.Table;
 import org.apache.hadoop.hive.metastore.api.hive_metastoreConstants;
-import org.apache.kudu.junit.RetryRule;
+import org.apache.kudu.test.junit.RetryRule;
 import org.apache.thrift.TException;
 import org.junit.After;
 import org.junit.Before;

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-jepsen/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-jepsen/build.gradle b/java/kudu-jepsen/build.gradle
index 1f4ccc9..4fe1bf4 100644
--- a/java/kudu-jepsen/build.gradle
+++ b/java/kudu-jepsen/build.gradle
@@ -32,7 +32,7 @@ repositories {
 
 dependencies {
   compile project(path: ":kudu-client", configuration: "shadow")
-  compile project(path: ":kudu-client", configuration: "shadowTest")
+  compile project(path: ":kudu-test-utils", configuration: "shadow")
   compile libs.clojure
   compile libs.clojureToolsCli
   compile libs.jepsen

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-mapreduce/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-mapreduce/build.gradle b/java/kudu-mapreduce/build.gradle
index 3789ed3..39ef80e 100644
--- a/java/kudu-mapreduce/build.gradle
+++ b/java/kudu-mapreduce/build.gradle
@@ -30,7 +30,7 @@ dependencies {
 
   optional libs.yetusAnnotations
 
-  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile project(path: ":kudu-test-utils", configuration: "shadow")
   testCompile libs.commonsIo
   testCompile libs.junit
   testCompile libs.log4j

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITInputFormatJob.java
----------------------------------------------------------------------
diff --git a/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITInputFormatJob.java b/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITInputFormatJob.java
index ef175b5..2808ed9 100644
--- a/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITInputFormatJob.java
+++ b/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITInputFormatJob.java
@@ -16,8 +16,8 @@
 // under the License.
 package org.apache.kudu.mapreduce;
 
+import static org.apache.kudu.test.ClientTestUtil.createFourTabletsTableWithNineRows;
 import static org.apache.kudu.test.KuduTestHarness.DEFAULT_SLEEP;
-import static org.apache.kudu.util.ClientTestUtil.createFourTabletsTableWithNineRows;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -32,8 +32,8 @@ import org.apache.hadoop.mapreduce.Job;
 import org.apache.hadoop.mapreduce.Mapper;
 import org.apache.hadoop.mapreduce.lib.output.NullOutputFormat;
 import org.apache.kudu.Schema;
+import org.apache.kudu.test.ClientTestUtil;
 import org.apache.kudu.test.KuduTestHarness;
-import org.apache.kudu.util.ClientTestUtil;
 import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableInputFormat.java
----------------------------------------------------------------------
diff --git a/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableInputFormat.java b/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableInputFormat.java
index 14a0c55..7aa4c6d 100644
--- a/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableInputFormat.java
+++ b/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableInputFormat.java
@@ -16,9 +16,9 @@
 // under the License.
 package org.apache.kudu.mapreduce;
 
+import static org.apache.kudu.test.ClientTestUtil.getBasicCreateTableOptions;
+import static org.apache.kudu.test.ClientTestUtil.getBasicSchema;
 import static org.apache.kudu.test.KuduTestHarness.DEFAULT_SLEEP;
-import static org.apache.kudu.util.ClientTestUtil.getBasicCreateTableOptions;
-import static org.apache.kudu.util.ClientTestUtil.getBasicSchema;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableOutputFormat.java
----------------------------------------------------------------------
diff --git a/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableOutputFormat.java b/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableOutputFormat.java
index f8a2b27..2719ebf 100644
--- a/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableOutputFormat.java
+++ b/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITKuduTableOutputFormat.java
@@ -16,9 +16,9 @@
 // under the License.
 package org.apache.kudu.mapreduce;
 
-import static org.apache.kudu.util.ClientTestUtil.countRowsInScan;
-import static org.apache.kudu.util.ClientTestUtil.getBasicCreateTableOptions;
-import static org.apache.kudu.util.ClientTestUtil.getBasicSchema;
+import static org.apache.kudu.test.ClientTestUtil.countRowsInScan;
+import static org.apache.kudu.test.ClientTestUtil.getBasicCreateTableOptions;
+import static org.apache.kudu.test.ClientTestUtil.getBasicSchema;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITOutputFormatJob.java
----------------------------------------------------------------------
diff --git a/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITOutputFormatJob.java b/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITOutputFormatJob.java
index 080f16a..ba3682e 100644
--- a/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITOutputFormatJob.java
+++ b/java/kudu-mapreduce/src/test/java/org/apache/kudu/mapreduce/ITOutputFormatJob.java
@@ -18,9 +18,9 @@ package org.apache.kudu.mapreduce;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.kudu.test.KuduTestHarness.DEFAULT_SLEEP;
-import static org.apache.kudu.util.ClientTestUtil.countRowsInScan;
-import static org.apache.kudu.util.ClientTestUtil.getBasicCreateTableOptions;
-import static org.apache.kudu.util.ClientTestUtil.getBasicSchema;
+import static org.apache.kudu.test.ClientTestUtil.countRowsInScan;
+import static org.apache.kudu.test.ClientTestUtil.getBasicCreateTableOptions;
+import static org.apache.kudu.test.ClientTestUtil.getBasicSchema;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-spark-tools/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-spark-tools/build.gradle b/java/kudu-spark-tools/build.gradle
index eb93f20..fb78e88 100644
--- a/java/kudu-spark-tools/build.gradle
+++ b/java/kudu-spark-tools/build.gradle
@@ -31,7 +31,7 @@ dependencies {
   provided libs.sparkSql
   provided libs.slf4jApi
 
-  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile project(path: ":kudu-test-utils", configuration: "shadow")
   testCompile project(path: ":kudu-spark", configuration: "test")
   testCompile libs.junit
   testCompile libs.log4j

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-spark/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-spark/build.gradle b/java/kudu-spark/build.gradle
index aa2c774..6a7d205 100644
--- a/java/kudu-spark/build.gradle
+++ b/java/kudu-spark/build.gradle
@@ -24,17 +24,15 @@ dependencies {
   compile libs.yetusAnnotations
 
   provided libs.scalaLibrary
-  provided libs.scalap
   provided libs.sparkCore
   provided libs.sparkSql
   provided libs.slf4jApi
 
 
-  testCompile project(path: ":kudu-client", configuration: "shadowTest")
+  testCompile project(path: ":kudu-test-utils", configuration: "shadow")
   testCompile libs.junit
   testCompile libs.scalatest
-  testCompile libs.sparkSqlTest
 }
 
 // Adjust the artifact name to include the spark and scala base versions.
-archivesBaseName = "kudu-spark${versions.sparkBase}_${versions.scalaBase}"
+archivesBaseName = "kudu-spark${versions.sparkBase}_${versions.scalaBase}"
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-test-utils/build.gradle
----------------------------------------------------------------------
diff --git a/java/kudu-test-utils/build.gradle b/java/kudu-test-utils/build.gradle
new file mode 100644
index 0000000..934f86d
--- /dev/null
+++ b/java/kudu-test-utils/build.gradle
@@ -0,0 +1,38 @@
+// 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.
+
+apply from: "$rootDir/gradle/shadow.gradle"
+
+dependencies {
+  compile project(path: ":kudu-client")
+  compile libs.guava
+
+  compileUnshaded libs.junit
+  compileUnshaded libs.slf4jApi
+
+  // Needed for CapturingLogAppender. Optional otherwise.
+  optional libs.log4j
+  optional libs.slf4jLog4j12
+
+  optional libs.jsr305
+  optional libs.yetusAnnotations
+}
+
+// kudu-test-utils has no public Javadoc.
+javadoc {
+  enabled = false
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/kudu/blob/15f1416f/java/kudu-test-utils/src/main/java/org/apache/kudu/test/CapturingLogAppender.java
----------------------------------------------------------------------
diff --git a/java/kudu-test-utils/src/main/java/org/apache/kudu/test/CapturingLogAppender.java b/java/kudu-test-utils/src/main/java/org/apache/kudu/test/CapturingLogAppender.java
new file mode 100644
index 0000000..81c9bc9
--- /dev/null
+++ b/java/kudu-test-utils/src/main/java/org/apache/kudu/test/CapturingLogAppender.java
@@ -0,0 +1,82 @@
+// 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.kudu.test;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+import com.google.common.base.Throwables;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Logger;
+import org.apache.log4j.SimpleLayout;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.yetus.audience.InterfaceStability;
+
+/**
+ * Test utility which wraps Log4j and captures all messages logged
+ * while it is attached. This can be useful for asserting that a particular
+ * message is (or is not) logged.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class CapturingLogAppender extends AppenderSkeleton {
+  private StringBuilder appended = new StringBuilder();
+  private static final Layout layout = new SimpleLayout();
+
+  @Override
+  public void close() {
+  }
+
+  @Override
+  public boolean requiresLayout() {
+    return false;
+  }
+
+  @Override
+  protected void append(LoggingEvent event) {
+    appended.append(layout.format(event));
+    if (event.getThrowableInformation() != null) {
+      appended.append(Throwables.getStackTraceAsString(
+          event.getThrowableInformation().getThrowable())).append("\n");
+    }
+  }
+
+  public String getAppendedText() {
+    return appended.toString();
+  }
+
+  /**
+   * Temporarily attach the capturing appender to the Log4j root logger.
+   * This can be used in a 'try-with-resources' block:
+   * <code>
+   *   try (Closeable c = capturer.attach()) {
+   *     ...
+   *   }
+   * </code>
+   */
+  public Closeable attach() {
+    Logger.getRootLogger().addAppender(this);
+    return new Closeable() {
+      @Override
+      public void close() throws IOException {
+        Logger.getRootLogger().removeAppender(CapturingLogAppender.this);
+      }
+    };
+  }
+}