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   *   http://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  package org.apache.maven.surefire.booter;
20  
21  import java.lang.management.ManagementFactory;
22  
23  import org.junit.jupiter.api.Test;
24  
25  import static org.assertj.core.api.Assertions.assertThat;
26  import static org.assertj.core.api.Assertions.assertThatThrownBy;
27  import static org.junit.jupiter.api.Assertions.assertThrows;
28  import static org.junit.jupiter.api.Assumptions.assumeTrue;
29  
30  /**
31   * Testing {@link ProcessChecker} via {@link ProcessChecker#of(String)}.
32   *
33   * @since 2.20.1
34   */
35  @SuppressWarnings("checkstyle:magicnumber")
36  public class ProcessCheckerTest {
37  
38      @Test
39      public void shouldHavePidAtBegin() {
40          String expectedPid =
41                  ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim();
42  
43          ProcessChecker checker = ProcessChecker.of(expectedPid);
44  
45          assertThat(checker).isNotNull();
46  
47          assertThat(checker.canUse()).isTrue();
48  
49          assertThat(checker.isProcessAlive()).isTrue();
50  
51          ProcessInfo processInfo = checker.processInfo();
52          assertThat(processInfo).isNotNull();
53          assertThat(processInfo.getPID()).isEqualTo(expectedPid);
54          assertThat(processInfo.getTime()).isGreaterThan(0L);
55      }
56  
57      @Test
58      public void shouldBeStopped() {
59          ProcessChecker checker = ProcessChecker.of("0");
60          checker.stop();
61  
62          assertThat(checker.canUse()).isFalse();
63  
64          IllegalStateException e = assertThrows(IllegalStateException.class, () -> checker.isProcessAlive());
65          assertThat(e.getMessage()).contains("irrelevant to call isProcessAlive()");
66      }
67  
68      @Test
69      public void exceptionCallIsProcessAlive() {
70          // FIXME DisabledOnJre when we migrate to junit5 and run on unix too
71          // winddows java 8 must depends on wwmc something available
72          double v = Double.parseDouble(System.getProperty("java.specification.version"));
73          assumeTrue(v >= 9.0);
74          ProcessChecker checker = ProcessChecker.of(Long.toString(Integer.MAX_VALUE));
75          checker.stop();
76          assertThatThrownBy(checker::isProcessAlive).isInstanceOf(IllegalStateException.class);
77      }
78  
79      @Test
80      public void shouldReturnNullForNullPpid() {
81          ProcessChecker checker = ProcessChecker.of(null);
82          assertThat(checker).isNull();
83      }
84  
85      @Test
86      public void shouldBeTypeNull() {
87          assertThat(ProcessCheckerType.toEnum(null)).isNull();
88  
89          assertThat(ProcessCheckerType.toEnum("   ")).isNull();
90  
91          assertThat(ProcessCheckerType.isValid(null)).isTrue();
92      }
93  
94      @Test
95      public void shouldBeException() {
96          IllegalArgumentException e =
97                  assertThrows(IllegalArgumentException.class, () -> ProcessCheckerType.toEnum("anything else"));
98          assertThat(e.getMessage()).contains("unknown process checker");
99      }
100 
101     @Test
102     public void shouldNotBeValid() {
103         assertThat(ProcessCheckerType.isValid("anything")).isFalse();
104     }
105 
106     @Test
107     public void shouldBeTypePing() {
108         assertThat(ProcessCheckerType.toEnum("ping")).isEqualTo(ProcessCheckerType.PING);
109 
110         assertThat(ProcessCheckerType.isValid("ping")).isTrue();
111 
112         assertThat(ProcessCheckerType.PING.getType()).isEqualTo("ping");
113     }
114 
115     @Test
116     public void shouldBeTypeNative() {
117         assertThat(ProcessCheckerType.toEnum("native")).isEqualTo(ProcessCheckerType.NATIVE);
118 
119         assertThat(ProcessCheckerType.isValid("native")).isTrue();
120 
121         assertThat(ProcessCheckerType.NATIVE.getType()).isEqualTo("native");
122     }
123 
124     @Test
125     public void shouldBeTypeAll() {
126         assertThat(ProcessCheckerType.toEnum("all")).isEqualTo(ProcessCheckerType.ALL);
127 
128         assertThat(ProcessCheckerType.isValid("all")).isTrue();
129 
130         assertThat(ProcessCheckerType.ALL.getType()).isEqualTo("all");
131     }
132 
133     @Test
134     public void shouldCreateCheckerForCurrentProcess() {
135         // Get current process PID using reflection to stay Java 8 compatible
136         String currentPid = getCurrentPid();
137         if (currentPid == null) {
138             // Skip test if we can't get PID
139             return;
140         }
141 
142         ProcessChecker checker = ProcessChecker.of(currentPid);
143 
144         assertThat(checker).isNotNull();
145         assertThat(checker.canUse()).isTrue();
146         assertThat(checker.isProcessAlive()).isTrue();
147         assertThat(checker.isStopped()).isFalse();
148     }
149 
150     @Test
151     public void shouldSelectProcessHandleCheckerOnJava9Plus() {
152         if (!ProcessChecker.isProcessHandleSupported()) {
153             // Skip test if ProcessHandle is not available (Java 8)
154             return;
155         }
156 
157         String currentPid = getCurrentPid();
158         if (currentPid == null) {
159             return;
160         }
161 
162         ProcessChecker checker = ProcessChecker.of(currentPid);
163         assertThat(checker.getClass().getSimpleName()).isEqualTo("ProcessHandleChecker");
164     }
165 
166     @Test
167     public void shouldStopChecker() {
168         String currentPid = getCurrentPid();
169         if (currentPid == null) {
170             return;
171         }
172 
173         ProcessChecker checker = ProcessChecker.of(currentPid);
174 
175         assertThat(checker.canUse()).isTrue();
176         assertThat(checker.isStopped()).isFalse();
177 
178         checker.stop();
179 
180         assertThat(checker.isStopped()).isTrue();
181         assertThat(checker.canUse()).isFalse();
182     }
183 
184     @Test
185     public void shouldDestroyActiveCommands() {
186         String currentPid = getCurrentPid();
187         if (currentPid == null) {
188             return;
189         }
190 
191         ProcessChecker checker = ProcessChecker.of(currentPid);
192         assertThat(checker.canUse()).isTrue();
193 
194         checker.destroyActiveCommands();
195 
196         assertThat(checker.isStopped()).isTrue();
197         assertThat(checker.canUse()).isFalse();
198     }
199 
200     @Test
201     public void shouldHandleNonExistentProcess() {
202         // Use an invalid PID that's unlikely to exist
203         ProcessChecker checker = ProcessChecker.of(Long.toString(Long.MAX_VALUE));
204 
205         assertThat(checker).isNotNull();
206 
207         assertThat(checker.canUse()).isTrue();
208 
209         assertThat(checker.isProcessAlive()).isFalse();
210     }
211 
212     /**
213      * Gets the current process PID in a way that works on both Java 8 and Java 9+.
214      */
215     private static String getCurrentPid() {
216         // Try ProcessHandle (Java 9+) first via reflection
217         try {
218             Class<?> processHandleClass = Class.forName("java.lang.ProcessHandle");
219             Object currentHandle = processHandleClass.getMethod("current").invoke(null);
220             Long pid = (Long) processHandleClass.getMethod("pid").invoke(currentHandle);
221             return String.valueOf(pid);
222         } catch (Exception e) {
223             // Fall back to ManagementFactory (works on Java 8)
224             try {
225                 String name = java.lang.management.ManagementFactory.getRuntimeMXBean()
226                         .getName();
227                 int atIndex = name.indexOf('@');
228                 if (atIndex > 0) {
229                     return name.substring(0, atIndex);
230                 }
231             } catch (Exception ex) {
232                 // Ignore
233             }
234         }
235         return null;
236     }
237 }