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