This is an automated email from the ASF dual-hosted git repository.

szetszwo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ratis.git


The following commit(s) were added to refs/heads/master by this push:
     new 54750b3e1 RATIS-1981. Avoid random ports in tests (#996)
54750b3e1 is described below

commit 54750b3e1dd0323071993685d8640f288a67d988
Author: Doroszlai, Attila <[email protected]>
AuthorDate: Wed Dec 20 16:26:43 2023 +0100

    RATIS-1981. Avoid random ports in tests (#996)
---
 .../main/java/org/apache/ratis/util/NetUtils.java  | 68 +++++++++++++++-------
 .../java/org/apache/ratis/util/TestNetUtils.java   | 25 +++++++-
 .../apache/ratis/server/impl/MiniRaftCluster.java  | 26 ++++-----
 .../datastream/TestNettyDataStreamWithMock.java    |  9 +--
 .../apache/ratis/grpc/util/TestGrpcZeroCopy.java   |  7 +--
 .../grpc/util/TestStreamObserverWithTimeout.java   |  6 +-
 6 files changed, 88 insertions(+), 53 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/util/NetUtils.java 
b/ratis-common/src/main/java/org/apache/ratis/util/NetUtils.java
index 39d0b76ca..a6ce5af79 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/NetUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/NetUtils.java
@@ -20,15 +20,14 @@ package org.apache.ratis.util;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.Closeable;
 import java.io.IOException;
-import java.io.UncheckedIOException;
 import java.net.*;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 public interface NetUtils {
   Logger LOG = LoggerFactory.getLogger(NetUtils.class);
@@ -123,21 +122,11 @@ public interface NetUtils {
    * @param count number of unique local addresses to create
    * @return {@code count} number of unique local addresses
    */
+  @Deprecated
   static List<InetSocketAddress> createLocalServerAddress(int count) {
     List<InetSocketAddress> list = new ArrayList<>(count);
-    List<ServerSocket> sockets = new ArrayList<>(count);
-    try {
-      for (int i = 0; i < count; i++) {
-        ServerSocket s = new ServerSocket();
-        sockets.add(s);
-        s.setReuseAddress(true);
-        s.bind(new InetSocketAddress(InetAddress.getByName(null), 0), 1);
-        list.add((InetSocketAddress) s.getLocalSocketAddress());
-      }
-    } catch (IOException e) {
-      throw new UncheckedIOException(e);
-    } finally {
-      IOUtils.cleanup(null, sockets.toArray(new Closeable[0]));
+    for (int i = 0; i < count; i++) {
+      list.add(new InetSocketAddress(LOCALHOST, getFreePort()));
     }
     return list;
   }
@@ -147,14 +136,9 @@ public interface NetUtils {
    * the loopback interface.
    * @return unique local address
    */
+  @Deprecated
   static InetSocketAddress createLocalServerAddress() {
-    try(ServerSocket s = new ServerSocket()) {
-      s.setReuseAddress(true);
-      s.bind(new InetSocketAddress(InetAddress.getByName(null), 0), 1);
-      return (InetSocketAddress) s.getLocalSocketAddress();
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
+    return new InetSocketAddress(LOCALHOST, getFreePort());
   }
 
   static String address2String(InetSocketAddress address) {
@@ -167,4 +151,44 @@ public interface NetUtils {
     }
     return b.append(':').append(address.getPort()).toString();
   }
+
+  String LOCALHOST = "localhost";
+
+  static String localhostWithFreePort() {
+    return LOCALHOST + ":" + getFreePort();
+  }
+
+  static int getFreePort() {
+    return PortAllocator.getFreePort();
+  }
+
+  /**
+   * Helper class to get free port avoiding randomness.
+   */
+  final class PortAllocator {
+
+    private static final int MIN_PORT = 15000;
+    private static final int MAX_PORT = 32000;
+    private static final AtomicInteger NEXT_PORT = new AtomicInteger(MIN_PORT);
+
+    private PortAllocator() {
+      // no instances
+    }
+
+    public static synchronized int getFreePort() {
+      while (true) {
+        int port = NEXT_PORT.getAndIncrement();
+        if (port > MAX_PORT) {
+          NEXT_PORT.set(MIN_PORT);
+          port = NEXT_PORT.getAndIncrement();
+        }
+        try (ServerSocket ignored = new ServerSocket(port)) {
+          return port;
+        } catch (IOException e) {
+          // will try next port
+        }
+      }
+    }
+  }
+
 }
diff --git a/ratis-common/src/test/java/org/apache/ratis/util/TestNetUtils.java 
b/ratis-common/src/test/java/org/apache/ratis/util/TestNetUtils.java
index 6fb85e263..2bed8e30d 100644
--- a/ratis-common/src/test/java/org/apache/ratis/util/TestNetUtils.java
+++ b/ratis-common/src/test/java/org/apache/ratis/util/TestNetUtils.java
@@ -20,17 +20,38 @@ package org.apache.ratis.util;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
-public class TestNetUtils {
+class TestNetUtils {
 
   @Test
-  public void createsUniqueAddresses() {
+  void createsUniqueAddresses() {
     for (int i = 0; i < 10; i++) {
       List<InetSocketAddress> addresses = 
NetUtils.createLocalServerAddress(100);
       
Assertions.assertEquals(addresses.stream().distinct().collect(Collectors.toList()),
 addresses);
     }
   }
+
+  @Test
+  void returnsUniquePorts() {
+    List<Integer> addresses = new ArrayList<>();
+    for (int i = 0; i < 10; i++) {
+      addresses.add(NetUtils.getFreePort());
+    }
+    
Assertions.assertEquals(addresses.stream().distinct().collect(Collectors.toList()),
 addresses);
+  }
+
+  @Test
+  void skipsUsedPort() throws IOException {
+    int port = NetUtils.getFreePort();
+    try (ServerSocket ignored = new ServerSocket(port + 1)) {
+      int nextPort = NetUtils.getFreePort();
+      Assertions.assertEquals(port + 2, nextPort);
+    }
+  }
 }
diff --git 
a/ratis-server/src/test/java/org/apache/ratis/server/impl/MiniRaftCluster.java 
b/ratis-server/src/test/java/org/apache/ratis/server/impl/MiniRaftCluster.java
index 795ca6d4f..f6dd6121c 100644
--- 
a/ratis-server/src/test/java/org/apache/ratis/server/impl/MiniRaftCluster.java
+++ 
b/ratis-server/src/test/java/org/apache/ratis/server/impl/MiniRaftCluster.java
@@ -64,7 +64,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -229,9 +228,10 @@ public abstract class MiniRaftCluster implements Closeable 
{
     }
 
     private int getPort(String address) {
-      final InetSocketAddress inetAddress = address != null?
-          NetUtils.createSocketAddr(address): 
NetUtils.createLocalServerAddress();
-      return inetAddress.getPort();
+      return Optional.ofNullable(address)
+          .map(NetUtils::createSocketAddr)
+          .map(InetSocketAddress::getPort)
+          .orElseGet(NetUtils::getFreePort);
     }
   }
 
@@ -248,14 +248,13 @@ public abstract class MiniRaftCluster implements 
Closeable {
   }
 
   public static RaftGroup initRaftGroup(Collection<String> ids, 
Collection<String> listenerIds) {
-    Iterator<InetSocketAddress> addresses = 
NetUtils.createLocalServerAddress(4 * (ids.size() + 
listenerIds.size())).iterator();
     Stream<RaftPeer> peer = ids.stream()
         .map(id -> RaftPeer.newBuilder().setId(id))
-        .map(p -> assignAddresses(p, addresses))
+        .map(MiniRaftCluster::assignAddresses)
         .map(RaftPeer.Builder::build);
     Stream<RaftPeer> listener = listenerIds.stream()
         .map(id -> RaftPeer.newBuilder().setId(id))
-        .map(p -> assignAddresses(p, addresses))
+        .map(MiniRaftCluster::assignAddresses)
         .map(p -> p.setStartupRole(RaftProtos.RaftPeerRole.LISTENER))
         .map(RaftPeer.Builder::build);
     final RaftPeer[] peers = Stream.concat(peer, 
listener).toArray(RaftPeer[]::new);
@@ -263,12 +262,12 @@ public abstract class MiniRaftCluster implements 
Closeable {
     return RaftGroup.valueOf(RaftGroupId.randomId(), peers);
   }
 
-  private static RaftPeer.Builder assignAddresses(RaftPeer.Builder builder, 
Iterator<InetSocketAddress> addresses) {
+  private static RaftPeer.Builder assignAddresses(RaftPeer.Builder builder) {
     return builder
-        .setAddress(addresses.next())
-        .setAdminAddress(addresses.next())
-        .setClientAddress(addresses.next())
-        .setDataStreamAddress(addresses.next());
+        .setAddress(NetUtils.localhostWithFreePort())
+        .setAdminAddress(NetUtils.localhostWithFreePort())
+        .setClientAddress(NetUtils.localhostWithFreePort())
+        .setDataStreamAddress(NetUtils.localhostWithFreePort());
   }
 
   private final Supplier<File> rootTestDir = JavaUtils.memoize(
@@ -468,11 +467,10 @@ public abstract class MiniRaftCluster implements 
Closeable {
     if (emptyPeer) {
       raftGroup = RaftGroup.valueOf(group.getGroupId(), 
Collections.emptyList());
     } else {
-      Iterator<InetSocketAddress> addresses = 
NetUtils.createLocalServerAddress(4 * ids.length).iterator();
       final Collection<RaftPeer> newPeers = 
StreamSupport.stream(peerIds.spliterator(), false)
           .map(id -> RaftPeer.newBuilder().setId(id)
               .setStartupRole(startRole))
-          .map(p -> assignAddresses(p, addresses))
+          .map(MiniRaftCluster::assignAddresses)
           .map(RaftPeer.Builder::build)
           .collect(Collectors.toSet());
       newPeers.addAll(group.getPeers());
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/datastream/TestNettyDataStreamWithMock.java
 
b/ratis-test/src/test/java/org/apache/ratis/datastream/TestNettyDataStreamWithMock.java
index 64fa59e40..27a1ee102 100644
--- 
a/ratis-test/src/test/java/org/apache/ratis/datastream/TestNettyDataStreamWithMock.java
+++ 
b/ratis-test/src/test/java/org/apache/ratis/datastream/TestNettyDataStreamWithMock.java
@@ -19,7 +19,6 @@
 package org.apache.ratis.datastream;
 
 import java.io.IOException;
-import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -47,12 +46,10 @@ import static org.mockito.Mockito.when;
 @Ignore
 public class TestNettyDataStreamWithMock extends DataStreamBaseTest {
   static RaftPeer newRaftPeer(RaftServer server) {
-    final InetSocketAddress rpc = NetUtils.createLocalServerAddress();
-    final int dataStreamPort = 
NettyConfigKeys.DataStream.port(server.getProperties());
     return RaftPeer.newBuilder()
         .setId(server.getId())
-        .setAddress(rpc)
-        
.setDataStreamAddress(NetUtils.createSocketAddrForHost(rpc.getHostName(), 
dataStreamPort))
+        .setAddress(NetUtils.localhostWithFreePort())
+        .setDataStreamAddress(NetUtils.localhostWithFreePort())
         .build();
   }
 
@@ -91,7 +88,7 @@ public class TestNettyDataStreamWithMock extends 
DataStreamBaseTest {
       RaftServer raftServer = mock(RaftServer.class);
       RaftPeerId peerId = RaftPeerId.valueOf("s" + i);
       RaftProperties properties = new RaftProperties();
-      NettyConfigKeys.DataStream.setPort(properties, 
NetUtils.createLocalServerAddress().getPort());
+      NettyConfigKeys.DataStream.setPort(properties, NetUtils.getFreePort());
       RaftConfigKeys.DataStream.setType(properties, 
SupportedDataStreamType.NETTY);
 
       when(raftServer.getProperties()).thenReturn(properties);
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/util/TestGrpcZeroCopy.java 
b/ratis-test/src/test/java/org/apache/ratis/grpc/util/TestGrpcZeroCopy.java
index a4592a118..032a9c1db 100644
--- a/ratis-test/src/test/java/org/apache/ratis/grpc/util/TestGrpcZeroCopy.java
+++ b/ratis-test/src/test/java/org/apache/ratis/grpc/util/TestGrpcZeroCopy.java
@@ -29,7 +29,6 @@ import org.apache.ratis.util.TraditionalBinaryPrefix;
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
@@ -110,11 +109,9 @@ public final class TestGrpcZeroCopy extends BaseTest {
   }
 
   void runTestZeroCopy() throws Exception {
-    final InetSocketAddress address = NetUtils.createLocalServerAddress();
-
-    try (GrpcZeroCopyTestServer server = new 
GrpcZeroCopyTestServer(address.getPort())) {
+    try (GrpcZeroCopyTestServer server = new 
GrpcZeroCopyTestServer(NetUtils.getFreePort())) {
       final int port = server.start();
-      try (GrpcZeroCopyTestClient client = new 
GrpcZeroCopyTestClient(address.getHostName(), port)) {
+      try (GrpcZeroCopyTestClient client = new 
GrpcZeroCopyTestClient(NetUtils.LOCALHOST, port)) {
         sendMessages(5, client, server);
         sendBinaries(11, client, server);
       }
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/grpc/util/TestStreamObserverWithTimeout.java
 
b/ratis-test/src/test/java/org/apache/ratis/grpc/util/TestStreamObserverWithTimeout.java
index 89349cc87..7a32fb96a 100644
--- 
a/ratis-test/src/test/java/org/apache/ratis/grpc/util/TestStreamObserverWithTimeout.java
+++ 
b/ratis-test/src/test/java/org/apache/ratis/grpc/util/TestStreamObserverWithTimeout.java
@@ -28,7 +28,6 @@ import org.junit.Assert;
 import org.junit.Test;
 import org.slf4j.event.Level;
 
-import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
@@ -80,15 +79,14 @@ public class TestStreamObserverWithTimeout extends BaseTest 
{
     LOG.info("slow = {}, {}", slow, type);
     final TimeDuration timeout = ONE_SECOND.multiply(0.5);
     final StreamObserverFactory function = type.createFunction(timeout);
-    final InetSocketAddress address = NetUtils.createLocalServerAddress();
 
     final List<String> messages = new ArrayList<>();
     for (int i = 0; i < 2 * slow; i++) {
       messages.add("m" + i);
     }
-    try (GrpcTestServer server = new GrpcTestServer(address.getPort(), slow, 
timeout)) {
+    try (GrpcTestServer server = new GrpcTestServer(NetUtils.getFreePort(), 
slow, timeout)) {
       final int port = server.start();
-      try (GrpcTestClient client = new GrpcTestClient(address.getHostName(), 
port, function)) {
+      try (GrpcTestClient client = new GrpcTestClient(NetUtils.LOCALHOST, 
port, function)) {
 
         final List<CompletableFuture<String>> futures = new ArrayList<>();
         for (String m : messages) {

Reply via email to