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

csutherl pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/10.1.x by this push:
     new 01870dbfba Skip tribes membership tests when IP multicast is 
unavailable (#1052)
01870dbfba is described below

commit 01870dbfbad797fbf725ce7d9dc3567039dfacd1
Author: Coty Sutherland <[email protected]>
AuthorDate: Tue Aug 25 14:38:19 2026 -0400

    Skip tribes membership tests when IP multicast is unavailable (#1052)
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 test/org/apache/catalina/tribes/TesterUtil.java    | 64 ++++++++++++++++++++++
 .../group/TestGroupChannelMemberArrival.java       |  3 +
 .../interceptors/TestDomainFilterInterceptor.java  |  4 ++
 .../interceptors/TestNonBlockingCoordinator.java   |  7 +++
 .../group/interceptors/TestOrderInterceptor.java   |  7 +++
 .../group/interceptors/TestTcpFailureDetector.java |  3 +
 6 files changed, 88 insertions(+)

diff --git a/test/org/apache/catalina/tribes/TesterUtil.java 
b/test/org/apache/catalina/tribes/TesterUtil.java
index 54a8db3957..78458cbd74 100644
--- a/test/org/apache/catalina/tribes/TesterUtil.java
+++ b/test/org/apache/catalina/tribes/TesterUtil.java
@@ -16,6 +16,15 @@
  */
 package org.apache.catalina.tribes;
 
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MulticastSocket;
+import java.net.StandardSocketOptions;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+
 import org.apache.catalina.tribes.group.interceptors.DomainFilterInterceptor;
 import org.apache.catalina.tribes.util.UUIDGenerator;
 
@@ -29,6 +38,61 @@ public class TesterUtil {
     }
 
 
+    /*
+     * Determines whether IP multicast is actually usable in the current
+     * environment. The tribes membership tests rely on the default
+     * {@link org.apache.catalina.tribes.membership.McastService} which
+     * discovers members via IP multicast. Some environments (containers,
+     * minimal CI images, hosts with multicast blocked by a firewall or with no
+     * multicast-capable network interface) cannot deliver multicast traffic. 
In
+     * those environments the affected tests would fail on a membership timeout
+     * through no fault of the code under test, so they should be skipped 
instead.
+     *
+     * This performs a functional check - it sends a multicast datagram to the
+     * default group over loopback and confirms it is received - rather than
+     * simply inspecting interface flags, so that it also detects environments
+     * where multicast sockets can be created but traffic is silently dropped.
+     * Any failure is treated as "multicast not available" so a hostile
+     * environment results in a skipped test rather than a spurious failure.
+     */
+    public static boolean isMulticastAvailable() {
+        InetAddress group;
+        try {
+            // Same default group as McastService
+            group = InetAddress.getByName("228.0.0.4");
+        } catch (UnknownHostException e) {
+            return false;
+        }
+
+        // Bind to an ephemeral port (not the McastService default) so this 
probe
+        // never clashes with a running membership service on the same host.
+        try (MulticastSocket socket = new MulticastSocket(0)) {
+            // Loopback must be enabled for the probe to receive its own 
packet.
+            socket.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, 
Boolean.TRUE);
+            socket.setSoTimeout(1000);
+
+            int port = socket.getLocalPort();
+            InetSocketAddress groupAddress = new InetSocketAddress(group, 
port);
+            // null network interface -> let the OS pick the default, mirroring
+            // McastServiceImpl when no bind address is configured.
+            socket.joinGroup(groupAddress, null);
+            try {
+                byte[] probe = 
"tomcat-tribes-multicast-probe".getBytes(StandardCharsets.UTF_8);
+                socket.send(new DatagramPacket(probe, probe.length, group, 
port));
+
+                DatagramPacket received = new DatagramPacket(new 
byte[probe.length], probe.length);
+                // Throws SocketTimeoutException if the packet is not 
delivered.
+                socket.receive(received);
+                return received.getLength() == probe.length;
+            } finally {
+                socket.leaveGroup(groupAddress, null);
+            }
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+
     /*
      * Configures a set of channels to use a random domain. Use to ensure that
      * multiple instance of the test suite do not interfere when running on the
diff --git 
a/test/org/apache/catalina/tribes/group/TestGroupChannelMemberArrival.java 
b/test/org/apache/catalina/tribes/group/TestGroupChannelMemberArrival.java
index 6163361579..58f9af09be 100644
--- a/test/org/apache/catalina/tribes/group/TestGroupChannelMemberArrival.java
+++ b/test/org/apache/catalina/tribes/group/TestGroupChannelMemberArrival.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -37,6 +38,8 @@ public class TestGroupChannelMemberArrival {
 
     @Before
     public void setUp() throws Exception {
+        Assume.assumeTrue("Skipping test - IP multicast is not available in 
this environment",
+                TesterUtil.isMulticastAvailable());
         for (int i = 0; i < channels.length; i++) {
             channels[i] = new GroupChannel();
             ((ReceiverBase) 
channels[i].getChannelReceiver()).setHost("localhost");
diff --git 
a/test/org/apache/catalina/tribes/group/interceptors/TestDomainFilterInterceptor.java
 
b/test/org/apache/catalina/tribes/group/interceptors/TestDomainFilterInterceptor.java
index 075ce5452b..4def88c6ec 100644
--- 
a/test/org/apache/catalina/tribes/group/interceptors/TestDomainFilterInterceptor.java
+++ 
b/test/org/apache/catalina/tribes/group/interceptors/TestDomainFilterInterceptor.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -27,6 +28,7 @@ import org.apache.catalina.tribes.Channel;
 import org.apache.catalina.tribes.ManagedChannel;
 import org.apache.catalina.tribes.Member;
 import org.apache.catalina.tribes.MembershipListener;
+import org.apache.catalina.tribes.TesterUtil;
 import org.apache.catalina.tribes.group.GroupChannel;
 import org.apache.catalina.tribes.util.UUIDGenerator;
 
@@ -37,6 +39,8 @@ public class TestDomainFilterInterceptor {
 
     @Before
     public void setUp() throws Exception {
+        Assume.assumeTrue("Skipping test - IP multicast is not available in 
this environment",
+                TesterUtil.isMulticastAvailable());
         for (int i = 0; i < channels.length; i++) {
             channels[i] = new GroupChannel();
             channels[i].getMembershipService().setPayload( ("Channel-" + (i + 
1)).getBytes("ASCII"));
diff --git 
a/test/org/apache/catalina/tribes/group/interceptors/TestNonBlockingCoordinator.java
 
b/test/org/apache/catalina/tribes/group/interceptors/TestNonBlockingCoordinator.java
index 1ad80db300..3c61c0952c 100644
--- 
a/test/org/apache/catalina/tribes/group/interceptors/TestNonBlockingCoordinator.java
+++ 
b/test/org/apache/catalina/tribes/group/interceptors/TestNonBlockingCoordinator.java
@@ -21,6 +21,7 @@ import java.util.logging.LogManager;
 
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -43,6 +44,8 @@ public class TestNonBlockingCoordinator {
 
     @Before
     public void setUp() throws Exception {
+        Assume.assumeTrue("Skipping test - IP multicast is not available in 
this environment",
+                TesterUtil.isMulticastAvailable());
         LogManager.getLogManager().getLogger(
                 
"org.apache.catalina.tribes.group.interceptors.TestNonBlockingCoordinator").setLevel(Level.ALL);
         try {
@@ -177,6 +180,10 @@ public class TestNonBlockingCoordinator {
     @After
     public void tearDown() throws Exception {
         log.info("tearDown");
+        if (channels == null) {
+            // setUp was skipped (e.g. multicast unavailable)
+            return;
+        }
         for ( int i=0; i<CHANNEL_COUNT; i++ ) {
             channels[i].stop(Channel.DEFAULT);
         }
diff --git 
a/test/org/apache/catalina/tribes/group/interceptors/TestOrderInterceptor.java 
b/test/org/apache/catalina/tribes/group/interceptors/TestOrderInterceptor.java
index 66d74b1ce0..35398458a2 100644
--- 
a/test/org/apache/catalina/tribes/group/interceptors/TestOrderInterceptor.java
+++ 
b/test/org/apache/catalina/tribes/group/interceptors/TestOrderInterceptor.java
@@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -48,6 +49,8 @@ public class TestOrderInterceptor {
 
     @Before
     public void setUp() throws Exception {
+        Assume.assumeTrue("Skipping test - IP multicast is not available in 
this environment",
+                TesterUtil.isMulticastAvailable());
         System.out.println("Setup");
         channels = new GroupChannel[channelCount];
         orderitcs = new OrderInterceptor[channelCount];
@@ -152,6 +155,10 @@ public class TestOrderInterceptor {
     @After
     public void tearDown() throws Exception {
         System.out.println("tearDown");
+        if (channels == null) {
+            // setUp was skipped (e.g. multicast unavailable)
+            return;
+        }
         for ( int i=0; i<channelCount; i++ ) {
             channels[i].stop(Channel.DEFAULT);
         }
diff --git 
a/test/org/apache/catalina/tribes/group/interceptors/TestTcpFailureDetector.java
 
b/test/org/apache/catalina/tribes/group/interceptors/TestTcpFailureDetector.java
index 7c8a87c699..fdcd7d6345 100644
--- 
a/test/org/apache/catalina/tribes/group/interceptors/TestTcpFailureDetector.java
+++ 
b/test/org/apache/catalina/tribes/group/interceptors/TestTcpFailureDetector.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -43,6 +44,8 @@ public class TestTcpFailureDetector {
 
     @Before
     public void setUp() throws Exception {
+        Assume.assumeTrue("Skipping test - IP multicast is not available in 
this environment",
+                TesterUtil.isMulticastAvailable());
         channel1 = new GroupChannel();
         channel2 = new GroupChannel();
         ((ReceiverBase) channel1.getChannelReceiver()).setHost("localhost");


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to