From 847c30b17a3571b7f4a29793e07c32f12a34b73f Mon Sep 17 00:00:00 2001 From: Mitch Gaffigan Date: Sat, 25 Jul 2026 17:53:06 -0500 Subject: [PATCH 1/4] Speed up TcpDispatcherTest to cut 90 seconds out of test runs Signed-off-by: Mitch Gaffigan --- .../connect/connectors/tcp/TcpDispatcher.java | 6 ++ .../connectors/tcp/TcpDispatcherTest.java | 85 +++++++++++-------- 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/server/src/main/java/com/mirth/connect/connectors/tcp/TcpDispatcher.java b/server/src/main/java/com/mirth/connect/connectors/tcp/TcpDispatcher.java index acdd0c86eb..16dd25a411 100644 --- a/server/src/main/java/com/mirth/connect/connectors/tcp/TcpDispatcher.java +++ b/server/src/main/java/com/mirth/connect/connectors/tcp/TcpDispatcher.java @@ -788,6 +788,12 @@ protected ServerSocket getServerSocket() { return serverSocket; } + protected int getServerModeSocketCount() { + synchronized (serverModeSockets) { + return serverModeSockets.size(); + } + } + private String getLocalAddress() { return TcpUtil.getFixedHost(replacer.replaceValues(connectorProperties.getLocalAddress(), getChannelId(), getChannel().getName())); } diff --git a/server/src/test/java/com/mirth/connect/connectors/tcp/TcpDispatcherTest.java b/server/src/test/java/com/mirth/connect/connectors/tcp/TcpDispatcherTest.java index ae37500313..8d94f83d04 100644 --- a/server/src/test/java/com/mirth/connect/connectors/tcp/TcpDispatcherTest.java +++ b/server/src/test/java/com/mirth/connect/connectors/tcp/TcpDispatcherTest.java @@ -16,6 +16,7 @@ import java.util.Map.Entry; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; @@ -52,6 +53,9 @@ public class TcpDispatcherTest { private static final String CONNECTOR_MAP_SUCCESSFUL_SENDS_KEY = "successfulSends"; private static final String CONNECTOR_MAP_ALL_RESPONSES_KEY = "allResponses"; private static final boolean PRINT_DEBUG_MESSAGES = false; + private static final long SOCKET_CONNECT_TIMEOUT_SECONDS = 5; + private static final int RESPONSE_TIMEOUT_MILLIS = 100; + private static final int LATE_RESPONSE_DELAY_MILLIS = 200; private static AtomicInteger incrementingPort = new AtomicInteger(9000); private static AtomicInteger incrementingSocketListenerId = new AtomicInteger(0); @@ -96,7 +100,6 @@ public void tearDown() throws Exception { dispatcher.stop(); log("Undeploying TCP Dispatcher..."); dispatcher.onUndeploy(); - Thread.sleep(3000); } } @@ -120,7 +123,18 @@ private void setupDispatcher(TcpDispatcherProperties props) throws Exception { dispatcher.onDeploy(); log("Starting TCP Dispatcher..."); dispatcher.start(); - Thread.sleep(1000); + } + + private void startSocketListener(SocketThread socketListenerThread) throws InterruptedException { + socketListenerThread.start(); + } + + private void awaitServerConnections(int expectedConnections) throws InterruptedException { + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(SOCKET_CONNECT_TIMEOUT_SECONDS); + while (((TestTcpDispatcher) dispatcher).getServerModeSocketCount() != expectedConnections && System.nanoTime() < deadline) { + Thread.sleep(10); + } + assertEquals(expectedConnections, ((TestTcpDispatcher) dispatcher).getServerModeSocketCount()); } private TcpDispatcherProperties createTcpDispatcherProperties() throws IOException { @@ -168,9 +182,8 @@ public void testMessageSent() throws Exception { Map socketResult = new ConcurrentHashMap<>(); int socketListenerId = getNextSocketListenerId(); - createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0).start(); - - Thread.sleep(1000); + startSocketListener(createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0)); + awaitServerConnections(1); log("Sending message..."); dispatcher.send(dispatcherProps, new ConnectorMessage(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, 1L, 1, TEST_SERVER_ID, Calendar.getInstance(), Status.PENDING)); @@ -192,9 +205,8 @@ public void testMLLPMessageSent() throws Exception { Map socketResult = new ConcurrentHashMap<>(); int socketListenerId = getNextSocketListenerId(); - createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0).start(); - - Thread.sleep(1000); + startSocketListener(createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0)); + awaitServerConnections(1); log("Sending message..."); dispatcher.send(dispatcherProps, new ConnectorMessage(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, 1L, 1, TEST_SERVER_ID, Calendar.getInstance(), Status.PENDING)); @@ -219,9 +231,9 @@ public void testMessageSentToMultipleClients() throws Exception { for (int i = 0; i < 3; i++) { int socketListenerId = getNextSocketListenerId(); socketListenerIds.add(socketListenerId); - createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0).start(); + startSocketListener(createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0)); } - Thread.sleep(1000); + awaitServerConnections(3); log("Sending message..."); dispatcher.send(dispatcherProps, new ConnectorMessage(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, 1L, 1, TEST_SERVER_ID, Calendar.getInstance(), Status.PENDING)); @@ -249,9 +261,9 @@ public void testMLLPMessageSentToMultipleClients() throws Exception { for (int i = 0; i < 3; i++) { int socketListenerId = getNextSocketListenerId(); socketListenerIds.add(socketListenerId); - createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0).start(); + startSocketListener(createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0)); } - Thread.sleep(1000); + awaitServerConnections(3); log("Sending message..."); dispatcher.send(dispatcherProps, new ConnectorMessage(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, 1L, 1, TEST_SERVER_ID, Calendar.getInstance(), Status.PENDING)); @@ -283,9 +295,9 @@ public void testMessageSentWhenOneOfMultipleClientsClosed() throws Exception { int socketListenerId = getNextSocketListenerId(); SocketThread socketListenerThread = createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0); socketListeners.put(socketListenerId, socketListenerThread); - socketListenerThread.start(); + startSocketListener(socketListenerThread); } - Thread.sleep(1000); + awaitServerConnections(3); Iterator> iter = socketListeners.entrySet().iterator(); SocketThread socketListenerThread = iter.next().getValue(); @@ -330,9 +342,9 @@ public void testMLLPMessageSentWhenOneOfMultipleClientsClosed() throws Exception int socketListenerId = getNextSocketListenerId(); SocketThread socketListenerThread = createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0); socketListeners.put(socketListenerId, socketListenerThread); - socketListenerThread.start(); + startSocketListener(socketListenerThread); } - Thread.sleep(1000); + awaitServerConnections(3); Iterator> iter = socketListeners.entrySet().iterator(); SocketThread socketListenerThread = iter.next().getValue(); @@ -380,9 +392,9 @@ public void testMaxConnections() throws Exception { int socketListenerId = getNextSocketListenerId(); SocketThread socketListenerThread = createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0); socketListeners.put(socketListenerId, socketListenerThread); - socketListenerThread.start(); + startSocketListener(socketListenerThread); } - Thread.sleep(1000); + awaitServerConnections(1); log("Sending message..."); ConnectorMessage message = new ConnectorMessage(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, 1L, 1, TEST_SERVER_ID, Calendar.getInstance(), Status.PENDING); @@ -440,9 +452,9 @@ public void testMessageResponseWhenAllConnectionsClosed() throws Exception { int socketListenerId = getNextSocketListenerId(); SocketThread socketListenerThread = createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0); socketListeners.put(socketListenerId, socketListenerThread); - socketListenerThread.start(); + startSocketListener(socketListenerThread); } - Thread.sleep(1000); + awaitServerConnections(3); for (SocketThread thread : socketListeners.values()) { thread.closeSocket(); @@ -484,9 +496,9 @@ public void testMLLPMessageResponseWhenAllConnectionsClosed() throws Exception { int socketListenerId = getNextSocketListenerId(); SocketThread socketListenerThread = createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0); socketListeners.put(socketListenerId, socketListenerThread); - socketListenerThread.start(); + startSocketListener(socketListenerThread); } - Thread.sleep(1000); + awaitServerConnections(3); for (SocketThread thread : socketListeners.values()) { thread.closeSocket(); @@ -522,14 +534,13 @@ public void testMLLPMessageResponseWhenAllConnectionsClosed() throws Exception { public void testQueueOnResponseTimeout() throws Exception { TcpDispatcherProperties props = createTcpDispatcherProperties(); props.setQueueOnResponseTimeout(true); - props.setResponseTimeout("1000"); + props.setResponseTimeout(String.valueOf(RESPONSE_TIMEOUT_MILLIS)); setupDispatcher(props); Map socketResult = new ConcurrentHashMap<>(); int socketListenerId = getNextSocketListenerId(); - createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, false, 2000).start(); - - Thread.sleep(1000); + startSocketListener(createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, false, LATE_RESPONSE_DELAY_MILLIS)); + awaitServerConnections(1); log("Sending message..."); ConnectorMessage message = new ConnectorMessage(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, 1L, 1, TEST_SERVER_ID, Calendar.getInstance(), Status.PENDING); Response response = dispatcher.send(dispatcherProps, message); @@ -554,14 +565,13 @@ public void testQueueOnResponseTimeout() throws Exception { public void testQueueOnResponseTimeoutIsFalse() throws Exception { TcpDispatcherProperties props = createTcpDispatcherProperties(); props.setQueueOnResponseTimeout(false); - props.setResponseTimeout("1000"); + props.setResponseTimeout(String.valueOf(RESPONSE_TIMEOUT_MILLIS)); setupDispatcher(props); Map socketResult = new ConcurrentHashMap<>(); int socketListenerId = getNextSocketListenerId(); - createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, false, 2000).start(); - - Thread.sleep(1000); + startSocketListener(createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, false, LATE_RESPONSE_DELAY_MILLIS)); + awaitServerConnections(1); log("Sending message..."); ConnectorMessage message = new ConnectorMessage(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, 1L, 1, TEST_SERVER_ID, Calendar.getInstance(), Status.PENDING); Response response = dispatcher.send(dispatcherProps, message); @@ -589,7 +599,7 @@ public void testQueueOnResponseTimeoutIsFalse() throws Exception { public void testWhenSendToOneOfMultipleClientsErrors() throws Exception { TcpDispatcherProperties props = createTcpDispatcherProperties(); props.setQueueOnResponseTimeout(false); - props.setResponseTimeout("1000"); + props.setResponseTimeout(String.valueOf(RESPONSE_TIMEOUT_MILLIS)); setupDispatcher(props); Map socketResult = new ConcurrentHashMap<>(); @@ -599,15 +609,15 @@ public void testWhenSendToOneOfMultipleClientsErrors() throws Exception { int socketListenerId = getNextSocketListenerId(); SocketThread socketListenerThread = createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 0); socketListeners.put(socketListenerId, socketListenerThread); - socketListenerThread.start(); + startSocketListener(socketListenerThread); } // Create a socket that will cause a timeout before it sends a response int socketListenerId = getNextSocketListenerId(); - SocketThread socketListenerThread = createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, 2000); + SocketThread socketListenerThread = createSocketListenerThread(socketListenerId, socketResult, dispatcherProps, true, LATE_RESPONSE_DELAY_MILLIS); socketListeners.put(socketListenerId, socketListenerThread); - socketListenerThread.start(); - Thread.sleep(1000); + startSocketListener(socketListenerThread); + awaitServerConnections(3); log("Sending message..."); ConnectorMessage message = new ConnectorMessage(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, 1L, 1, TEST_SERVER_ID, Calendar.getInstance(), Status.PENDING); @@ -660,7 +670,7 @@ public void testServerSocketAllInterfaces() throws Exception { @Test public void testServerSocketUnknownHost(){ TcpDispatcherProperties props = new TcpDispatcherProperties(); - props.setLocalAddress("111.1.1.1"); + props.setLocalAddress("256.256.256.256"); props.setLocalPort("6666"); props.setServerMode(true); @@ -743,13 +753,14 @@ public void closeSocket() { logError("Error closing socket: " + e.getMessage()); } } - + @Override public void run() { try { socket = SocketUtil.createSocket(new DefaultTcpConfiguration()); socket.setKeepAlive(false); socket.setSoLinger(false, 0); + socket.setTcpNoDelay(true); SocketUtil.connectSocket(socket, "127.0.0.1", Integer.parseInt(dispatcherProps.getLocalPort()), 0); log("Client socket created with remote port: " + socket.getPort() + " and local port: " + socket.getLocalPort()); From 85d2306b88dcedd81126e80a8ca5f4f8b70f7182 Mon Sep 17 00:00:00 2001 From: Mitch Gaffigan Date: Sat, 25 Jul 2026 18:31:22 -0500 Subject: [PATCH 2/4] Reduce Thread.sleep in TcpReceiverTest Signed-off-by: Mitch Gaffigan --- .../mirth/connect/connectors/tcp/TcpReceiver.java | 8 ++++++-- .../connect/connectors/tcp/TcpReceiverTest.java | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/com/mirth/connect/connectors/tcp/TcpReceiver.java b/server/src/main/java/com/mirth/connect/connectors/tcp/TcpReceiver.java index abf67e0f85..66f48433db 100644 --- a/server/src/main/java/com/mirth/connect/connectors/tcp/TcpReceiver.java +++ b/server/src/main/java/com/mirth/connect/connectors/tcp/TcpReceiver.java @@ -895,7 +895,7 @@ private void createServerSocket() throws IOException { throw e; } else { try { - Thread.sleep(1000); + Thread.sleep(getBindRetryInterval()); } catch (InterruptedException e2) { Thread.currentThread().interrupt(); } @@ -903,7 +903,11 @@ private void createServerSocket() throws IOException { } } } - + + protected long getBindRetryInterval() { + return 1000L; + } + protected ServerSocket getServerSocket() { return serverSocket; } diff --git a/server/src/test/java/com/mirth/connect/connectors/tcp/TcpReceiverTest.java b/server/src/test/java/com/mirth/connect/connectors/tcp/TcpReceiverTest.java index 11d88eb9e1..90b1d97d48 100644 --- a/server/src/test/java/com/mirth/connect/connectors/tcp/TcpReceiverTest.java +++ b/server/src/test/java/com/mirth/connect/connectors/tcp/TcpReceiverTest.java @@ -36,7 +36,12 @@ public class TcpReceiverTest { private static final String TEST_CHANNEL_ID = UUID.randomUUID().toString(); private static final String TEST_SERVER_ID = UUID.randomUUID().toString(); private static final String TEST_CHANNEL_NAME = "Test TCP Listener Channel"; - + /* + * The production bind retry interval is one second, and testServerSocketUnknownHost exhausts all + * ten attempts. Shortening it in the test subclass keeps the retry path covered without the wait. + */ + private static final long BIND_RETRY_INTERVAL_MILLIS = 10; + private static Logger logger = LogManager.getLogger(TcpReceiverTest.class); private TcpReceiver receiver; private TcpReceiverProperties receiverProps; @@ -74,7 +79,6 @@ public void tearDown() throws Exception { receiver.stop(); logger.debug("Undeploying TCP Receiver..."); receiver.onUndeploy(); - Thread.sleep(1000); } } @@ -85,8 +89,8 @@ private void setupReceiver(TcpReceiverProperties props) throws Exception { logger.debug("Deploying TCP Receiver..."); receiver.onDeploy(); logger.debug("Starting TCP Receiver..."); + // start() binds the server socket synchronously, so there is nothing to wait for afterwards. receiver.start(); - Thread.sleep(1000); } private TcpReceiver createTcpReceiver(TcpReceiverProperties receiverProps) { @@ -167,6 +171,11 @@ public TestTcpReceiver(String channelId, String serverId, Integer metaDataId, Tc protected String getConfigurationClass() { return "com.mirth.connect.connectors.tcp.DefaultTcpConfiguration"; } + + @Override + protected long getBindRetryInterval() { + return BIND_RETRY_INTERVAL_MILLIS; + } } private static class TestChannel extends Channel { From e1e736dd24959c90e717f53e26e7c88ddbee44ef Mon Sep 17 00:00:00 2001 From: Mitch Gaffigan Date: Sat, 25 Jul 2026 18:38:29 -0500 Subject: [PATCH 3/4] Reduce Thread.sleep in PropertiesConfigurationUtilTest Signed-off-by: Mitch Gaffigan --- .../core/PropertiesConfigurationUtil.java | 22 ++++++- .../core/PropertiesConfigurationUtilTest.java | 57 ++++++++++++++----- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/server/src/main/java/com/mirth/connect/client/core/PropertiesConfigurationUtil.java b/server/src/main/java/com/mirth/connect/client/core/PropertiesConfigurationUtil.java index d29736d9f9..bd2c162ce6 100644 --- a/server/src/main/java/com/mirth/connect/client/core/PropertiesConfigurationUtil.java +++ b/server/src/main/java/com/mirth/connect/client/core/PropertiesConfigurationUtil.java @@ -28,6 +28,16 @@ public class PropertiesConfigurationUtil { + /** + * How long the reloading detector waits before it will stat the file again. This matches the + * default used by FileHandlerReloadingDetector. + */ + private static final long DEFAULT_RELOADING_REFRESH_DELAY_MILLIS = 5000; + + /** How often the periodic trigger asks the reloading controller to check for changes. */ + private static final long DEFAULT_RELOAD_TRIGGER_PERIOD = 1; + private static final TimeUnit DEFAULT_RELOAD_TRIGGER_PERIOD_UNIT = TimeUnit.SECONDS; + public static FileBasedConfigurationBuilder createBuilder() { return new Configurations().propertiesBuilder(getDefaultParameters()); } @@ -66,7 +76,11 @@ public static ReloadingFileBasedConfigurationBuilder cr } public static ReloadingFileBasedConfigurationBuilder createReloadingBuilder(File file, boolean commaDelimited) { - PropertiesBuilderParameters params = getDefaultParameters().setFile(file); + return createReloadingBuilder(file, commaDelimited, DEFAULT_RELOADING_REFRESH_DELAY_MILLIS); + } + + public static ReloadingFileBasedConfigurationBuilder createReloadingBuilder(File file, boolean commaDelimited, long reloadingRefreshDelayMillis) { + PropertiesBuilderParameters params = getDefaultParameters().setFile(file).setReloadingRefreshDelay(reloadingRefreshDelayMillis); if (commaDelimited) { params.setListDelimiterHandler(new DefaultListDelimiterHandler(',')); } @@ -74,7 +88,11 @@ public static ReloadingFileBasedConfigurationBuilder cr } public static PeriodicReloadingTrigger createReloadTrigger(ReloadingFileBasedConfigurationBuilder builder) { - return new PeriodicReloadingTrigger(builder.getReloadingController(), null, 1, TimeUnit.SECONDS); + return createReloadTrigger(builder, DEFAULT_RELOAD_TRIGGER_PERIOD, DEFAULT_RELOAD_TRIGGER_PERIOD_UNIT); + } + + public static PeriodicReloadingTrigger createReloadTrigger(ReloadingFileBasedConfigurationBuilder builder, long period, TimeUnit unit) { + return new PeriodicReloadingTrigger(builder.getReloadingController(), null, period, unit); } public static void saveTo(PropertiesConfiguration config, File file) throws ConfigurationException { diff --git a/server/src/test/java/com/mirth/connect/client/core/PropertiesConfigurationUtilTest.java b/server/src/test/java/com/mirth/connect/client/core/PropertiesConfigurationUtilTest.java index 2f41c7f43f..9dd7f6e539 100644 --- a/server/src/test/java/com/mirth/connect/client/core/PropertiesConfigurationUtilTest.java +++ b/server/src/test/java/com/mirth/connect/client/core/PropertiesConfigurationUtilTest.java @@ -18,6 +18,7 @@ import java.io.InputStream; import java.util.Iterator; import java.util.UUID; +import java.util.concurrent.TimeUnit; import org.apache.commons.configuration2.PropertiesConfiguration; import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; @@ -29,6 +30,16 @@ public class PropertiesConfigurationUtilTest { + /* + * The production reload path is deliberately slow: a one second trigger period on top of the + * five second refresh delay that FileHandlerReloadingDetector defaults to. Driving both down to + * milliseconds keeps the reload behavior under test while removing seven seconds of sleeping. + */ + private static final long RELOAD_REFRESH_DELAY_MILLIS = 10; + private static final long RELOAD_TRIGGER_PERIOD_MILLIS = 10; + private static final long RELOAD_TIMEOUT_MILLIS = 10000; + private static final long QUIET_PERIOD_MILLIS = 200; + @Test public void testCreateBuilder1() throws Exception { FileBasedConfigurationBuilder builder = PropertiesConfigurationUtil.createBuilder(); @@ -110,31 +121,49 @@ public void testCreateReloadingBuilder1() throws Exception { File file = new File(UUID.randomUUID().toString()); file.createNewFile(); - ReloadingFileBasedConfigurationBuilder builder = PropertiesConfigurationUtil.createReloadingBuilder(file); + ReloadingFileBasedConfigurationBuilder builder = PropertiesConfigurationUtil.createReloadingBuilder(file, false, RELOAD_REFRESH_DELAY_MILLIS); PropertiesConfiguration config = builder.getConfiguration(); assertTrue(config.getListDelimiterHandler() == DisabledListDelimiterHandler.INSTANCE); assertFalse(config.getKeys().hasNext()); - PeriodicReloadingTrigger trigger = PropertiesConfigurationUtil.createReloadTrigger(builder); + PeriodicReloadingTrigger trigger = PropertiesConfigurationUtil.createReloadTrigger(builder, RELOAD_TRIGGER_PERIOD_MILLIS, TimeUnit.MILLISECONDS); trigger.start(); - Thread.sleep(2000); - config = builder.getConfiguration(); - assertFalse(config.getKeys().hasNext()); + try { + // The file is still empty, so many trigger cycles must not invent any content. + Thread.sleep(QUIET_PERIOD_MILLIS); + assertFalse(builder.getConfiguration().getKeys().hasNext()); - FileUtils.writeStringToFile(file, getTestFile(), "UTF-8"); - PropertiesConfiguration config2 = PropertiesConfigurationUtil.create(file); - verifyTestProperties(config2); + FileUtils.writeStringToFile(file, getTestFile(), "UTF-8"); + PropertiesConfiguration config2 = PropertiesConfigurationUtil.create(file); + verifyTestProperties(config2); - Thread.sleep(5000); - config = builder.getConfiguration(); - verifyTestProperties(config); + verifyTestProperties(awaitReloadedConfiguration(builder)); + } finally { + trigger.shutdown(); + file.delete(); + } + } - trigger.shutdown(); - file.delete(); + /** + * Polls the builder until the trigger has picked up the changed file. The reload normally lands + * within a few trigger periods; the timeout only exists so a genuine failure reports as a failed + * assertion instead of hanging. + */ + private static PropertiesConfiguration awaitReloadedConfiguration(ReloadingFileBasedConfigurationBuilder builder) throws Exception { + long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(RELOAD_TIMEOUT_MILLIS); + PropertiesConfiguration config = builder.getConfiguration(); + + while (!config.getKeys().hasNext() && System.nanoTime() < deadline) { + Thread.sleep(RELOAD_TRIGGER_PERIOD_MILLIS); + config = builder.getConfiguration(); + } + + assertTrue("Configuration was not reloaded after the file changed", config.getKeys().hasNext()); + return config; } - + @Test public void testCreateReloadingBuilderCommaDelimited() throws Exception { File file = new File(UUID.randomUUID().toString()); From a1a584984decdf171df5a08102e75c8b163f8fb6 Mon Sep 17 00:00:00 2001 From: Mitch Gaffigan Date: Sat, 25 Jul 2026 19:00:16 -0500 Subject: [PATCH 4/4] Allow parallel tests Signed-off-by: Mitch Gaffigan --- build.gradle | 5 +++++ .../connect/connectors/tcp/TcpReceiverTest.java | 14 +++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 622069b107..d9de29d36a 100644 --- a/build.gradle +++ b/build.gradle @@ -264,6 +264,11 @@ subprojects { maxHeapSize = '2048m' jvmArgs '-Xshare:off' include '**/*Test.class' + /* + * Past ~4 forks the per-fork JVM startup cost (inflated by -Xshare:off above) outweighs the + * gain, since only a handful of classes take more than a second. Override with -PtestForks=N. + */ + maxParallelForks = (project.findProperty('testForks') ?: Math.max(1, Math.min(4, Runtime.runtime.availableProcessors().intdiv(2)))) as int } if (rootProject.coverageEnabled) { diff --git a/server/src/test/java/com/mirth/connect/connectors/tcp/TcpReceiverTest.java b/server/src/test/java/com/mirth/connect/connectors/tcp/TcpReceiverTest.java index 90b1d97d48..04ef0c505f 100644 --- a/server/src/test/java/com/mirth/connect/connectors/tcp/TcpReceiverTest.java +++ b/server/src/test/java/com/mirth/connect/connectors/tcp/TcpReceiverTest.java @@ -113,40 +113,40 @@ private TcpReceiver createTcpReceiver(TcpReceiverProperties receiverProps) { public void testServerSocketLocalHost1() throws Exception { TcpReceiverProperties props = new TcpReceiverProperties(); props.getListenerConnectorProperties().setHost("127.0.0.1"); - props.getListenerConnectorProperties().setPort("6666"); + props.getListenerConnectorProperties().setPort("6667"); setupReceiver(props); assertEquals("127.0.0.1", receiver.getServerSocket().getInetAddress().getHostAddress()); - assertEquals(6666, receiver.getServerSocket().getLocalPort()); + assertEquals(6667, receiver.getServerSocket().getLocalPort()); } @Test public void testServerSocketLocalHost2() throws Exception { TcpReceiverProperties props = new TcpReceiverProperties(); props.getListenerConnectorProperties().setHost("localhost"); - props.getListenerConnectorProperties().setPort("6666"); + props.getListenerConnectorProperties().setPort("6667"); setupReceiver(props); assertEquals("localhost", receiver.getServerSocket().getInetAddress().getHostName()); - assertEquals(6666, receiver.getServerSocket().getLocalPort()); + assertEquals(6667, receiver.getServerSocket().getLocalPort()); } @Test public void testServerSocketAllInterfaces() throws Exception { TcpReceiverProperties props = new TcpReceiverProperties(); props.getListenerConnectorProperties().setHost("0.0.0.0"); - props.getListenerConnectorProperties().setPort("6666"); + props.getListenerConnectorProperties().setPort("6667"); setupReceiver(props); assertEquals("0.0.0.0", receiver.getServerSocket().getInetAddress().getHostAddress()); - assertEquals(6666, receiver.getServerSocket().getLocalPort()); + assertEquals(6667, receiver.getServerSocket().getLocalPort()); } @Test public void testServerSocketUnknownHost(){ TcpReceiverProperties props = new TcpReceiverProperties(); props.getListenerConnectorProperties().setHost("111.1.1.1"); - props.getListenerConnectorProperties().setPort("6666"); + props.getListenerConnectorProperties().setPort("6667"); boolean exceptionThrown = false; try {