View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.commons.exec.issues;
21  
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.io.File;
25  
26  import org.apache.commons.exec.AbstractExecTest;
27  import org.apache.commons.exec.CommandLine;
28  import org.apache.commons.exec.DefaultExecutor;
29  import org.apache.commons.exec.ExecuteException;
30  import org.apache.commons.exec.ExecuteWatchdog;
31  import org.apache.commons.exec.Executor;
32  import org.junit.jupiter.api.Disabled;
33  import org.junit.jupiter.api.Test;
34  
35  /**
36   * Test EXEC-60 (https://issues.apache.org/jira/browse/EXEC-60).
37   */
38  class Exec60Test extends AbstractExecTest {
39  
40      private final Executor exec = DefaultExecutor.builder().get();
41      private final File pingScript = resolveTestScript("ping");
42  
43      /**
44       * Possible deadlock when a process is terminating at the same time its timing out. Please note that a successful test is no proof that the issues was
45       * indeed fixed.
46       */
47      @Disabled("The test is fragile and might fail out of the blue")
48      @Test
49      void testExec60() throws Exception {
50  
51          final int start = 0;
52          final int seconds = 1;
53          final int offsetMultiplier = 1;
54          final int maxRetries = 180;
55          int processTerminatedCounter = 0;
56          int watchdogKilledProcessCounter = 0;
57          final CommandLine cmdLine = new CommandLine(pingScript);
58          cmdLine.addArgument(Integer.toString(seconds + 1)); // need to add "1" to wait the requested number of seconds
59  
60          final long startTime = System.currentTimeMillis();
61          for (int offset = start; offset <= maxRetries; offset++) {
62              // wait progressively longer for process to complete
63              // tricky to get this test right. We want to try and catch the process while it is terminating,
64              // so we increase the timeout gradually until the test terminates normally.
65              // However, if the increase is too gradual, we never wait long enough for any test to exit normally
66              final ExecuteWatchdog watchdog = new ExecuteWatchdog(seconds * 1000 + offset * offsetMultiplier);
67              exec.setWatchdog(watchdog);
68              try {
69                  exec.execute(cmdLine);
70                  processTerminatedCounter++;
71                  // System.out.println(offset + ": process has terminated: " + watchdog.killedProcess());
72                  if (processTerminatedCounter > 5) {
73                      break;
74                  }
75              } catch (final ExecuteException ex) {
76                  // System.out.println(offset + ": process was killed: " + watchdog.killedProcess());
77                  assertTrue(watchdog.killedProcess(), "Watchdog killed the process");
78                  watchdogKilledProcessCounter++;
79              }
80          }
81  
82          final long avg = (System.currentTimeMillis() - startTime) / (watchdogKilledProcessCounter + processTerminatedCounter);
83          System.out.println("Processes terminated: " + processTerminatedCounter + " killed: " + watchdogKilledProcessCounter + " Multiplier: " + offsetMultiplier
84                  + " MaxRetries: " + maxRetries + " Elapsed (avg ms): " + avg);
85          assertTrue(processTerminatedCounter > 0, "Not a single process terminated on its own");
86          assertTrue(watchdogKilledProcessCounter > 0, "Not a single process was killed by the watch dog");
87      }
88  }