mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-17 11:45:05 +02:00
Libre threads (#5303)
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
@@ -37,21 +37,50 @@ public class ProcessExecutorTest {
|
||||
|
||||
@Test
|
||||
public void testRunCommandWithOutputHandling_Error() {
|
||||
// Mock the command to execute
|
||||
// Test with a command that will fail to execute (non-existent command)
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("nonexistent-command");
|
||||
command.add("nonexistent-command-that-does-not-exist");
|
||||
|
||||
// Execute the command and expect an IOException
|
||||
IOException thrown =
|
||||
// Execute the command and expect an IOException (command not found)
|
||||
assertThrows(
|
||||
IOException.class, () -> processExecutor.runCommandWithOutputHandling(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunCommandWithOutputHandling_PathTraversal() {
|
||||
// Test that path traversal is blocked
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("../../../etc/passwd");
|
||||
|
||||
// Execute the command and expect an IllegalArgumentException
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
IllegalArgumentException.class,
|
||||
() -> processExecutor.runCommandWithOutputHandling(command));
|
||||
|
||||
// Check the exception message to ensure it indicates the command was not found
|
||||
// Check the exception message
|
||||
String errorMessage = thrown.getMessage();
|
||||
assertTrue(
|
||||
errorMessage.contains("error=2")
|
||||
|| errorMessage.contains("No such file or directory"),
|
||||
errorMessage.contains("path traversal"),
|
||||
"Unexpected error message: " + errorMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunCommandWithOutputHandling_NullByte() {
|
||||
// Test that null bytes are blocked
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("test\0command");
|
||||
|
||||
// Execute the command and expect an IllegalArgumentException
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> processExecutor.runCommandWithOutputHandling(command));
|
||||
|
||||
// Check the exception message
|
||||
String errorMessage = thrown.getMessage();
|
||||
assertTrue(
|
||||
errorMessage.contains("invalid characters"),
|
||||
"Unexpected error message: " + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
package stirling.software.common.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
|
||||
public class UnoServerPoolTest {
|
||||
|
||||
@Test
|
||||
void testEmptyPool() throws InterruptedException {
|
||||
UnoServerPool pool = new UnoServerPool(Collections.emptyList());
|
||||
assertTrue(pool.isEmpty(), "Pool with empty list should be empty");
|
||||
|
||||
UnoServerPool.UnoServerLease lease = pool.acquireEndpoint();
|
||||
assertNotNull(lease, "Should return a default lease for empty pool");
|
||||
assertNotNull(lease.getEndpoint(), "Default lease should have an endpoint");
|
||||
lease.close(); // Should not throw
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSingleEndpointAcquireRelease() throws InterruptedException {
|
||||
List<ApplicationProperties.ProcessExecutor.UnoServerEndpoint> endpoints =
|
||||
createEndpoints(1);
|
||||
UnoServerPool pool = new UnoServerPool(endpoints);
|
||||
assertFalse(pool.isEmpty(), "Pool should not be empty");
|
||||
|
||||
UnoServerPool.UnoServerLease lease = pool.acquireEndpoint();
|
||||
assertNotNull(lease, "Should acquire endpoint");
|
||||
assertEquals("127.0.0.1", lease.getEndpoint().getHost());
|
||||
assertEquals(2003, lease.getEndpoint().getPort());
|
||||
|
||||
lease.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleEndpointsDistribution() throws InterruptedException {
|
||||
List<ApplicationProperties.ProcessExecutor.UnoServerEndpoint> endpoints =
|
||||
createEndpoints(3);
|
||||
UnoServerPool pool = new UnoServerPool(endpoints);
|
||||
|
||||
List<Integer> portsUsed = new ArrayList<>();
|
||||
|
||||
// Acquire all endpoints
|
||||
try (UnoServerPool.UnoServerLease lease1 = pool.acquireEndpoint();
|
||||
UnoServerPool.UnoServerLease lease2 = pool.acquireEndpoint();
|
||||
UnoServerPool.UnoServerLease lease3 = pool.acquireEndpoint()) {
|
||||
|
||||
portsUsed.add(lease1.getEndpoint().getPort());
|
||||
portsUsed.add(lease2.getEndpoint().getPort());
|
||||
portsUsed.add(lease3.getEndpoint().getPort());
|
||||
|
||||
// All three endpoints should be in use (different ports)
|
||||
assertEquals(3, portsUsed.stream().distinct().count(), "Should use all 3 endpoints");
|
||||
}
|
||||
// All released after try-with-resources
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConcurrentAccess() throws InterruptedException {
|
||||
int endpointCount = 3;
|
||||
int threadCount = 10;
|
||||
List<ApplicationProperties.ProcessExecutor.UnoServerEndpoint> endpoints =
|
||||
createEndpoints(endpointCount);
|
||||
UnoServerPool pool = new UnoServerPool(endpoints);
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
||||
CountDownLatch startLatch = new CountDownLatch(1);
|
||||
CountDownLatch doneLatch = new CountDownLatch(threadCount);
|
||||
AtomicInteger successCount = new AtomicInteger(0);
|
||||
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
executor.submit(
|
||||
() -> {
|
||||
try {
|
||||
startLatch.await(); // Wait for all threads to be ready
|
||||
UnoServerPool.UnoServerLease lease = pool.acquireEndpoint();
|
||||
assertNotNull(lease, "Should acquire endpoint");
|
||||
Thread.sleep(10); // Simulate work
|
||||
lease.close();
|
||||
successCount.incrementAndGet();
|
||||
} catch (Exception e) {
|
||||
fail("Thread failed: " + e.getMessage());
|
||||
} finally {
|
||||
doneLatch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startLatch.countDown(); // Start all threads
|
||||
boolean finished = doneLatch.await(5, TimeUnit.SECONDS);
|
||||
executor.shutdown();
|
||||
|
||||
assertTrue(finished, "All threads should complete within timeout");
|
||||
assertEquals(
|
||||
threadCount, successCount.get(), "All threads should successfully acquire/release");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBlockingBehavior() throws InterruptedException {
|
||||
List<ApplicationProperties.ProcessExecutor.UnoServerEndpoint> endpoints =
|
||||
createEndpoints(2);
|
||||
UnoServerPool pool = new UnoServerPool(endpoints);
|
||||
|
||||
// Acquire both endpoints
|
||||
UnoServerPool.UnoServerLease lease1 = pool.acquireEndpoint();
|
||||
UnoServerPool.UnoServerLease lease2 = pool.acquireEndpoint();
|
||||
|
||||
AtomicInteger acquired = new AtomicInteger(0);
|
||||
CountDownLatch acquireLatch = new CountDownLatch(1);
|
||||
|
||||
// Try to acquire a third endpoint in separate thread (should block)
|
||||
Thread blockingThread =
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
acquireLatch.countDown(); // Signal we're about to block
|
||||
UnoServerPool.UnoServerLease lease3 = pool.acquireEndpoint();
|
||||
acquired.incrementAndGet();
|
||||
lease3.close();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
|
||||
blockingThread.start();
|
||||
acquireLatch.await(); // Wait for thread to start
|
||||
Thread.sleep(100); // Give it time to block
|
||||
|
||||
// Should still be 0 because thread is blocked
|
||||
assertEquals(0, acquired.get(), "Third acquire should be blocked");
|
||||
|
||||
// Release one endpoint
|
||||
lease1.close();
|
||||
Thread.sleep(100); // Give blocked thread time to acquire
|
||||
|
||||
// Now the third acquire should succeed
|
||||
assertEquals(1, acquired.get(), "Third acquire should succeed after release");
|
||||
|
||||
lease2.close();
|
||||
blockingThread.join(1000);
|
||||
assertFalse(blockingThread.isAlive(), "Thread should complete");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEndpointReuse() throws InterruptedException {
|
||||
List<ApplicationProperties.ProcessExecutor.UnoServerEndpoint> endpoints =
|
||||
createEndpoints(1);
|
||||
UnoServerPool pool = new UnoServerPool(endpoints);
|
||||
|
||||
int port1, port2;
|
||||
|
||||
try (UnoServerPool.UnoServerLease lease1 = pool.acquireEndpoint()) {
|
||||
port1 = lease1.getEndpoint().getPort();
|
||||
}
|
||||
|
||||
try (UnoServerPool.UnoServerLease lease2 = pool.acquireEndpoint()) {
|
||||
port2 = lease2.getEndpoint().getPort();
|
||||
}
|
||||
|
||||
assertEquals(port1, port2, "Should reuse the same endpoint after release");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHostLocationAndProtocol() throws InterruptedException {
|
||||
List<ApplicationProperties.ProcessExecutor.UnoServerEndpoint> endpoints = new ArrayList<>();
|
||||
ApplicationProperties.ProcessExecutor.UnoServerEndpoint endpoint =
|
||||
new ApplicationProperties.ProcessExecutor.UnoServerEndpoint();
|
||||
endpoint.setHost("remote.server");
|
||||
endpoint.setPort(8080);
|
||||
endpoint.setHostLocation("remote");
|
||||
endpoint.setProtocol("https");
|
||||
endpoints.add(endpoint);
|
||||
|
||||
UnoServerPool pool = new UnoServerPool(endpoints);
|
||||
|
||||
try (UnoServerPool.UnoServerLease lease = pool.acquireEndpoint()) {
|
||||
assertEquals("remote.server", lease.getEndpoint().getHost());
|
||||
assertEquals(8080, lease.getEndpoint().getPort());
|
||||
assertEquals("remote", lease.getEndpoint().getHostLocation());
|
||||
assertEquals("https", lease.getEndpoint().getProtocol());
|
||||
}
|
||||
}
|
||||
|
||||
private List<ApplicationProperties.ProcessExecutor.UnoServerEndpoint> createEndpoints(
|
||||
int count) {
|
||||
List<ApplicationProperties.ProcessExecutor.UnoServerEndpoint> endpoints = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
ApplicationProperties.ProcessExecutor.UnoServerEndpoint endpoint =
|
||||
new ApplicationProperties.ProcessExecutor.UnoServerEndpoint();
|
||||
endpoint.setHost("127.0.0.1");
|
||||
endpoint.setPort(2003 + (i * 2));
|
||||
endpoints.add(endpoint);
|
||||
}
|
||||
return endpoints;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user