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