Copilot commented on code in PR #3149:
URL: https://github.com/apache/tika/pull/3149#discussion_r3968654291


##########
tika-core/src/test/java/org/apache/tika/utils/ProcessUtilsTest.java:
##########
@@ -133,4 +134,21 @@ public void 
testCheckCommandCustomTimeoutBoundsASlowCommand() {
         assertTrue(elapsed < 4_000,
                 "checkCommandWithTimeout must honor its own timeout, not the 
default; took " + elapsed + "ms");
     }
+
+    @Test
+    public void testExecuteFailsFastIfTimeoutIsZero() throws Exception {
+        assumeFalse(SystemUtils.IS_OS_WINDOWS);
+
+        ProcessBuilder pb = new ProcessBuilder("sleep", "5");
+        ParseContext context = new ParseContext();
+        context.set(TimeoutLimits.class, new TimeoutLimits(0, 0));
+
+        long start = System.currentTimeMillis();
+        FileProcessResult result = ProcessUtils.execute(pb, context, 5_000L, 
1000, 1000);
+        long elapsed = System.currentTimeMillis() - start;
+
+        assertTrue(result.isTimeout(), "a process with a 0 timeout should 
timeout immediately without starting");
+        assertEquals(0, result.getGrantedTimeoutMillis(), "the process should 
not have been granted any timeout; got " + result.getGrantedTimeoutMillis() + 
"ms");
+        assertTrue(elapsed < 4_000, "fast path should return without spawning; 
took " +  elapsed + "ms");

Review Comment:
   This test doesn’t actually verify that the subprocess was not started—on the 
old behavior (start then immediately kill), `elapsed` could still be well under 
4s, so the assertion may pass even if the process is spawned. To make this 
regression-proof, use a command that would create an observable side effect if 
it starts (e.g., `sh -c 'echo started > <tempfile>; sleep 5'`) and assert the 
side effect did *not* occur when granted timeout is 0.



##########
tika-core/src/main/java/org/apache/tika/utils/ProcessUtils.java:
##########
@@ -442,4 +448,16 @@ public static boolean waitForWithHeartbeat(Process p, 
ParseContext context, long
         }
     }
 
+    private static FileProcessResult failFastIfNoGrantedTimeout(long 
requestedTimeoutMillis, long grantedTimeoutMillis) {
+        if (grantedTimeoutMillis <= 0) {
+            FileProcessResult result = new FileProcessResult();
+            result.isTimeout = true;
+            result.requestedTimeoutMillis = requestedTimeoutMillis;
+            result.grantedTimeoutMillis = grantedTimeoutMillis;

Review Comment:
   The new behavior treats *any* `grantedTimeoutMillis <= 0` as an immediate 
timeout. Please document (e.g., method-level comment) that negative values are 
also considered 'no time budget' here, so future changes to timeout semantics 
(if any) don’t accidentally reintroduce process spawning for `<= 0` grants.



##########
tika-core/src/main/java/org/apache/tika/utils/ProcessUtils.java:
##########
@@ -146,6 +146,12 @@ public static FileProcessResult execute(ProcessBuilder pb, 
ParseContext context,
         long grantedTimeoutMillis = context == null
                 ? requestedTimeoutMillis
                 : 
ParseTimeout.getOrCreate(context).budgetFor(requestedTimeoutMillis);
+
+        FileProcessResult result = 
failFastIfNoGrantedTimeout(requestedTimeoutMillis, grantedTimeoutMillis);
+        if (result != null) {
+            return result;
+        }

Review Comment:
   Returning `null` as a sentinel from `failFastIfNoGrantedTimeout` makes the 
control flow easier to misuse later. Consider changing the helper to return a 
boolean (and construct the `FileProcessResult` inline) or return an 
`Optional<FileProcessResult>` to make the contract explicit.



##########
tika-core/src/main/java/org/apache/tika/utils/ProcessUtils.java:
##########
@@ -442,4 +448,16 @@ public static boolean waitForWithHeartbeat(Process p, 
ParseContext context, long
         }
     }
 
+    private static FileProcessResult failFastIfNoGrantedTimeout(long 
requestedTimeoutMillis, long grantedTimeoutMillis) {
+        if (grantedTimeoutMillis <= 0) {
+            FileProcessResult result = new FileProcessResult();
+            result.isTimeout = true;
+            result.requestedTimeoutMillis = requestedTimeoutMillis;
+            result.grantedTimeoutMillis = grantedTimeoutMillis;
+
+            return result;
+        }
+
+        return null;

Review Comment:
   Returning `null` as a sentinel from `failFastIfNoGrantedTimeout` makes the 
control flow easier to misuse later. Consider changing the helper to return a 
boolean (and construct the `FileProcessResult` inline) or return an 
`Optional<FileProcessResult>` to make the contract explicit.



##########
tika-core/src/test/java/org/apache/tika/utils/ProcessUtilsTest.java:
##########
@@ -133,4 +134,21 @@ public void 
testCheckCommandCustomTimeoutBoundsASlowCommand() {
         assertTrue(elapsed < 4_000,
                 "checkCommandWithTimeout must honor its own timeout, not the 
default; took " + elapsed + "ms");
     }
+
+    @Test
+    public void testExecuteFailsFastIfTimeoutIsZero() throws Exception {
+        assumeFalse(SystemUtils.IS_OS_WINDOWS);
+
+        ProcessBuilder pb = new ProcessBuilder("sleep", "5");
+        ParseContext context = new ParseContext();
+        context.set(TimeoutLimits.class, new TimeoutLimits(0, 0));
+
+        long start = System.currentTimeMillis();
+        FileProcessResult result = ProcessUtils.execute(pb, context, 5_000L, 
1000, 1000);
+        long elapsed = System.currentTimeMillis() - start;

Review Comment:
   Using `System.currentTimeMillis()` for elapsed-time assertions can be flaky 
(wall-clock adjustments) and the 4s threshold is very loose for a 'fail fast' 
path. Prefer `System.nanoTime()` for timing and consider a tighter bound (or 
rely on an observable side effect as suggested) to reduce the chance of false 
positives/negatives on CI.



##########
tika-core/src/test/java/org/apache/tika/utils/ProcessUtilsTest.java:
##########
@@ -133,4 +134,21 @@ public void 
testCheckCommandCustomTimeoutBoundsASlowCommand() {
         assertTrue(elapsed < 4_000,
                 "checkCommandWithTimeout must honor its own timeout, not the 
default; took " + elapsed + "ms");
     }
+
+    @Test
+    public void testExecuteFailsFastIfTimeoutIsZero() throws Exception {
+        assumeFalse(SystemUtils.IS_OS_WINDOWS);
+
+        ProcessBuilder pb = new ProcessBuilder("sleep", "5");
+        ParseContext context = new ParseContext();
+        context.set(TimeoutLimits.class, new TimeoutLimits(0, 0));
+
+        long start = System.currentTimeMillis();
+        FileProcessResult result = ProcessUtils.execute(pb, context, 5_000L, 
1000, 1000);
+        long elapsed = System.currentTimeMillis() - start;
+
+        assertTrue(result.isTimeout(), "a process with a 0 timeout should 
timeout immediately without starting");
+        assertEquals(0, result.getGrantedTimeoutMillis(), "the process should 
not have been granted any timeout; got " + result.getGrantedTimeoutMillis() + 
"ms");
+        assertTrue(elapsed < 4_000, "fast path should return without spawning; 
took " +  elapsed + "ms");

Review Comment:
   Using `System.currentTimeMillis()` for elapsed-time assertions can be flaky 
(wall-clock adjustments) and the 4s threshold is very loose for a 'fail fast' 
path. Prefer `System.nanoTime()` for timing and consider a tighter bound (or 
rely on an observable side effect as suggested) to reduce the chance of false 
positives/negatives on CI.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to