Repository: hbase
Updated Branches:
  refs/heads/master 23ec9e290 -> 1a6ec1bac


HBASE-14784 Port conflict is not resolved in 
HBaseTestingUtility.randomFreePort() (Youngjoon Kim)


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/1a6ec1ba
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/1a6ec1ba
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/1a6ec1ba

Branch: refs/heads/master
Commit: 1a6ec1bac65fa04a30a57891fb66dd248f3bed9c
Parents: 23ec9e2
Author: stack <[email protected]>
Authored: Tue Nov 10 09:53:30 2015 -1000
Committer: stack <[email protected]>
Committed: Tue Nov 10 09:53:30 2015 -1000

----------------------------------------------------------------------
 .../hadoop/hbase/HBaseTestingUtility.java       | 99 +++++++++++++-------
 .../hadoop/hbase/TestHBaseTestingUtility.java   | 34 ++++++-
 2 files changed, 99 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/1a6ec1ba/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
index de79bdf..5bb25db 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
@@ -203,9 +203,6 @@ public class HBaseTestingUtility extends 
HBaseCommonTestingUtility {
   /** Filesystem URI used for map-reduce mini-cluster setup */
   private static String FS_URI;
 
-  /** A set of ports that have been claimed using {@link #randomFreePort()}. */
-  private static final Set<Integer> takenRandomPorts = new HashSet<Integer>();
-
   /** Compression algorithms to use in parameterized JUnit 4 tests */
   public static final List<Object[]> COMPRESSION_ALGORITHMS_PARAMETERIZED =
     Arrays.asList(new Object[][] {
@@ -3429,41 +3426,77 @@ public class HBaseTestingUtility extends 
HBaseCommonTestingUtility {
     return table;
   }
 
-  private static final int MIN_RANDOM_PORT = 0xc000;
-  private static final int MAX_RANDOM_PORT = 0xfffe;
   private static Random random = new Random();
 
-  /**
-   * Returns a random port. These ports cannot be registered with IANA and are
-   * intended for dynamic allocation (see http://bit.ly/dynports).
-   */
-  public static int randomPort() {
-    return MIN_RANDOM_PORT
-        + random.nextInt(MAX_RANDOM_PORT - MIN_RANDOM_PORT);
-  }
+  private static final PortAllocator portAllocator = new PortAllocator(random);
 
-  /**
-   * Returns a random free port and marks that port as taken. Not thread-safe. 
Expected to be
-   * called from single-threaded test setup code/
-   */
   public static int randomFreePort() {
-    int port = 0;
-    do {
-      port = randomPort();
-      if (takenRandomPorts.contains(port)) {
-        port = 0;
-        continue;
-      }
-      takenRandomPorts.add(port);
+    return portAllocator.randomFreePort();
+  }
 
-      try {
-        ServerSocket sock = new ServerSocket(port);
-        sock.close();
-      } catch (IOException ex) {
-        port = 0;
-      }
-    } while (port == 0);
-    return port;
+  static class PortAllocator {
+    private static final int MIN_RANDOM_PORT = 0xc000;
+    private static final int MAX_RANDOM_PORT = 0xfffe;
+
+    /** A set of ports that have been claimed using {@link #randomFreePort()}. 
*/
+    private final Set<Integer> takenRandomPorts = new HashSet<Integer>();
+
+    private final Random random;
+    private final AvailablePortChecker portChecker;
+
+    public PortAllocator(Random random) {
+      this.random = random;
+      this.portChecker = new AvailablePortChecker() {
+        public boolean available(int port) {
+          try {
+            ServerSocket sock = new ServerSocket(port);
+            sock.close();
+            return true;
+          } catch (IOException ex) {
+            return false;
+          }
+        }
+      };
+    }
+
+    public PortAllocator(Random random, AvailablePortChecker portChecker) {
+      this.random = random;
+      this.portChecker = portChecker;
+    }
+
+    /**
+     * Returns a random free port and marks that port as taken. Not 
thread-safe. Expected to be
+     * called from single-threaded test setup code/
+     */
+    public int randomFreePort() {
+      int port = 0;
+      do {
+        port = randomPort();
+        if (takenRandomPorts.contains(port)) {
+          port = 0;
+          continue;
+        }
+        takenRandomPorts.add(port);
+
+        if (!portChecker.available(port)) {
+          port = 0;
+        }
+      } while (port == 0);
+      return port;
+    }
+
+    /**
+     * Returns a random port. These ports cannot be registered with IANA and 
are
+     * intended for dynamic allocation (see http://bit.ly/dynports).
+     */
+    private int randomPort() {
+      return MIN_RANDOM_PORT
+          + random.nextInt(MAX_RANDOM_PORT - MIN_RANDOM_PORT);
+    }
+
+    interface AvailablePortChecker {
+      boolean available(int port);
+    }
   }
 
 

http://git-wip-us.apache.org/repos/asf/hbase/blob/1a6ec1ba/hbase-server/src/test/java/org/apache/hadoop/hbase/TestHBaseTestingUtility.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/TestHBaseTestingUtility.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/TestHBaseTestingUtility.java
index dd09c37..10c9d86 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/TestHBaseTestingUtility.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/TestHBaseTestingUtility.java
@@ -21,6 +21,7 @@ package org.apache.hadoop.hbase;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 import org.apache.commons.logging.Log;
@@ -40,9 +41,13 @@ import org.apache.hadoop.hdfs.MiniDFSCluster;
 import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.io.File;
 import java.util.List;
+import java.util.Random;
 
 /**
  * Test our testing utility class
@@ -385,5 +390,32 @@ public class TestHBaseTestingUtility {
     assertTrue(hbt.cleanupTestDir());
   }
 
-}
+  @Test public void testResolvePortConflict() throws Exception {
+    // raises port conflict between 1st call and 2nd call of randomPort() by 
mocking Random object
+    Random random = Mockito.mock(Random.class);
+    Mockito.when(random.nextInt(Mockito.any(Integer.class)))
+      .thenAnswer(new Answer<Integer>() {
+        int[] numbers = { 1, 1, 2 };
+        int count = 0;
+
+        @Override
+        public Integer answer(InvocationOnMock invocation) {
+          int ret = numbers[count];
+          count++;
+          return ret;
+        }
+      });
+
+    HBaseTestingUtility.PortAllocator.AvailablePortChecker portChecker =
+      
Mockito.mock(HBaseTestingUtility.PortAllocator.AvailablePortChecker.class);
+    
Mockito.when(portChecker.available(Mockito.any(Integer.class))).thenReturn(true);
 
+    HBaseTestingUtility.PortAllocator portAllocator =
+      new HBaseTestingUtility.PortAllocator(random, portChecker);
+
+    int port1 = portAllocator.randomFreePort();
+    int port2 = portAllocator.randomFreePort();
+    assertNotEquals(port1, port2);
+    Mockito.verify(random, 
Mockito.times(3)).nextInt(Mockito.any(Integer.class));
+  }
+}

Reply via email to