1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.surefire.booter;
20
21 import javax.annotation.Nonnull;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InterruptedIOException;
26 import java.lang.management.ManagementFactory;
27 import java.util.Random;
28 import java.util.regex.Matcher;
29
30 import org.apache.maven.surefire.api.booter.DumpErrorSingleton;
31 import org.junit.After;
32 import org.junit.Before;
33 import org.junit.Rule;
34 import org.junit.Test;
35 import org.junit.rules.ExpectedException;
36 import org.junit.rules.TemporaryFolder;
37
38 import static java.nio.charset.StandardCharsets.US_ASCII;
39 import static java.nio.file.Files.readAllBytes;
40 import static java.util.concurrent.TimeUnit.SECONDS;
41 import static org.apache.maven.surefire.booter.ProcessInfo.unixProcessInfo;
42 import static org.apache.maven.surefire.booter.ProcessInfo.windowsProcessInfo;
43 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_UNIX;
44 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
45 import static org.assertj.core.api.Assertions.assertThat;
46 import static org.hamcrest.CoreMatchers.is;
47 import static org.hamcrest.CoreMatchers.not;
48 import static org.hamcrest.CoreMatchers.notNullValue;
49 import static org.junit.Assert.fail;
50 import static org.junit.Assume.assumeThat;
51 import static org.junit.Assume.assumeTrue;
52 import static org.powermock.reflect.Whitebox.invokeMethod;
53 import static org.powermock.reflect.Whitebox.setInternalState;
54
55
56
57
58
59
60
61 @SuppressWarnings("checkstyle:magicnumber")
62 public class PpidCheckerTest {
63 private static final Random RND = new Random();
64
65 @Rule
66 public final ExpectedException exceptions = ExpectedException.none();
67
68 @Rule
69 public final TemporaryFolder tempFolder = new TemporaryFolder();
70
71 private File reportsDir;
72 private String dumpFileName;
73
74 @Before
75 public void initTmpFile() {
76 reportsDir = tempFolder.getRoot();
77 dumpFileName = "surefire-" + RND.nextLong();
78 }
79
80 @After
81 public void deleteTmpFiles() {
82 tempFolder.delete();
83 }
84
85 @Test
86 public void canExecuteUnixPs() {
87 assumeTrue(IS_OS_UNIX);
88 assertThat(PpidChecker.canExecuteUnixPs())
89 .as("Surefire should be tested on real box OS, e.g. Ubuntu or FreeBSD.")
90 .isTrue();
91 }
92
93 @Test
94 public void shouldHavePidAtBegin() {
95 String expectedPid =
96 ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim();
97
98 PpidChecker checker = new PpidChecker(expectedPid);
99 ProcessInfo processInfo = IS_OS_UNIX ? checker.unix() : checker.windows();
100
101 assertThat(processInfo).isNotNull();
102
103 assertThat(checker.canUse()).isTrue();
104
105 assertThat(checker.isProcessAlive()).isTrue();
106
107 assertThat(processInfo.getPID()).isEqualTo(expectedPid);
108
109 assertThat(processInfo.getTime()).isGreaterThan(0L);
110 }
111
112 @Test
113 public void shouldHavePid() throws Exception {
114 String expectedPid =
115 ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim();
116
117 PpidChecker checker = new PpidChecker(expectedPid);
118 setInternalState(
119 checker,
120 "parentProcessInfo",
121 IS_OS_UNIX
122 ? unixProcessInfo(expectedPid, 0L)
123 : windowsProcessInfo(expectedPid, windowsProcessStartTime(checker)));
124
125
126 SECONDS.sleep(1L);
127
128 ProcessInfo processInfo = IS_OS_UNIX ? checker.unix() : checker.windows();
129
130 assertThat(processInfo).isNotNull();
131
132 assertThat(checker.canUse()).isTrue();
133
134 assertThat(checker.isProcessAlive()).isTrue();
135
136 assertThat(processInfo.getPID()).isEqualTo(expectedPid);
137
138 assertThat(processInfo.getTime()).isGreaterThan(0L);
139
140 assertThat(checker.toString())
141 .contains("ppid=" + expectedPid)
142 .contains("stopped=false")
143 .contains("invalid=false")
144 .contains("error=false");
145
146 checker.destroyActiveCommands();
147 assertThat(checker.canUse()).isFalse();
148 assertThat((boolean) invokeMethod(checker, "isStopped")).isTrue();
149 }
150
151 @Test
152 public void shouldBeStopped() {
153 PpidChecker checker = new PpidChecker("0");
154 checker.stop();
155
156 assertThat(checker.canUse()).isFalse();
157
158 exceptions.expect(IllegalStateException.class);
159 exceptions.expectMessage("irrelevant to call isProcessAlive()");
160
161 checker.isProcessAlive();
162
163 fail("this test should throw exception");
164 }
165
166 @Test
167 public void shouldBeStoppedCheckerWithError() throws Exception {
168 String expectedPid =
169 ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim();
170 DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName);
171
172 PpidChecker checker = new PpidChecker(expectedPid);
173 checker.stop();
174
175 ProcessInfo processInfo = IS_OS_UNIX ? checker.unix() : checker.windows();
176 assertThat(processInfo.isError()).isTrue();
177
178 String error = new String(readAllBytes(new File(reportsDir, dumpFileName + ".dump").toPath()));
179
180 assertThat(error).contains("<<exit>> <<0>>").contains("<<stopped>> <<true>>");
181 }
182
183 @Test
184 public void shouldBeEmptyDump() throws Exception {
185 String expectedPid =
186 ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim();
187 DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName);
188
189 PpidChecker checker = new PpidChecker(expectedPid);
190
191 try {
192 Thread.currentThread().interrupt();
193
194 ProcessInfo processInfo = IS_OS_UNIX ? checker.unix() : checker.windows();
195
196 Thread.interrupted();
197 assertThat(processInfo.isError()).isTrue();
198
199 File dumpFile = new File(reportsDir, dumpFileName + ".dump");
200 if (dumpFile.exists()) {
201 String error = new String(readAllBytes(dumpFile.toPath()));
202
203 assertThat(error).contains("<<exit>>").contains("<<stopped>> <<false>>");
204 }
205 } finally {
206
207 Thread.interrupted();
208 }
209 }
210
211 @Test
212 public void shouldStartedProcessThrowInterruptedException() throws Exception {
213 String expectedPid =
214 ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim();
215 DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName);
216
217 PpidChecker checker = new PpidChecker(expectedPid);
218
219 PpidChecker.ProcessInfoConsumer consumer = checker.new ProcessInfoConsumer(US_ASCII.name()) {
220 @Nonnull
221 @Override
222 ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception {
223 throw new InterruptedException();
224 }
225 };
226
227 String[] cmd =
228 IS_OS_WINDOWS ? new String[] {"CMD", "/A", "/X", "/C", "dir"} : new String[] {"/bin/sh", "-c", "ls"};
229
230 assertThat(consumer.execute(cmd).isError()).isTrue();
231 assertThat(new File(reportsDir, dumpFileName + ".dump")).doesNotExist();
232 }
233
234 @Test
235 public void shouldStartedProcessThrowInterruptedIOException() throws Exception {
236 String expectedPid =
237 ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim();
238 DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName);
239
240 PpidChecker checker = new PpidChecker(expectedPid);
241
242 PpidChecker.ProcessInfoConsumer consumer = checker.new ProcessInfoConsumer(US_ASCII.name()) {
243 @Nonnull
244 @Override
245 ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception {
246 throw new InterruptedIOException();
247 }
248 };
249
250 String[] cmd =
251 IS_OS_WINDOWS ? new String[] {"CMD", "/A", "/X", "/C", "dir"} : new String[] {"/bin/sh", "-c", "ls"};
252
253 assertThat(consumer.execute(cmd).isError()).isTrue();
254 assertThat(new File(reportsDir, dumpFileName + ".dump")).doesNotExist();
255 }
256
257 @Test
258 public void shouldStartedProcessThrowIOException() throws Exception {
259 String expectedPid =
260 ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim();
261 DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName);
262
263 PpidChecker checker = new PpidChecker(expectedPid);
264
265 PpidChecker.ProcessInfoConsumer consumer = checker.new ProcessInfoConsumer(US_ASCII.name()) {
266 @Nonnull
267 @Override
268 ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception {
269 throw new IOException("wrong command");
270 }
271 };
272
273 String[] cmd =
274 IS_OS_WINDOWS ? new String[] {"CMD", "/A", "/X", "/C", "dir"} : new String[] {"/bin/sh", "-c", "ls"};
275
276 assertThat(consumer.execute(cmd).isError()).isTrue();
277
278 File dumpFile = new File(reportsDir, dumpFileName + ".dump");
279
280 String error = new String(readAllBytes(dumpFile.toPath()));
281
282 assertThat(error).contains(IOException.class.getName()).contains("wrong command");
283 }
284
285 @Test
286 public void shouldNotFindSuchPID() {
287 PpidChecker checker = new PpidChecker("1000000");
288 setInternalState(checker, "parentProcessInfo", ProcessInfo.ERR_PROCESS_INFO);
289
290 assertThat(checker.canUse()).isFalse();
291
292 exceptions.expect(IllegalStateException.class);
293 exceptions.expectMessage("irrelevant to call isProcessAlive()");
294
295 checker.isProcessAlive();
296
297 fail("this test should throw exception");
298 }
299
300 @Test
301 public void shouldNotBeAlive() {
302 PpidChecker checker = new PpidChecker("1000000");
303
304 assertThat(checker.canUse()).isTrue();
305
306 assertThat(checker.isProcessAlive()).isFalse();
307 }
308
309 @Test
310 public void shouldParseEtime() {
311 Matcher m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("38 1234567890");
312 assertThat(m.matches()).isFalse();
313
314 m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("05:38 1234567890");
315 assertThat(m.matches()).isTrue();
316 assertThat(PpidChecker.fromDays(m)).isEqualTo(0L);
317 assertThat(PpidChecker.fromHours(m)).isEqualTo(0L);
318 assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L);
319 assertThat(PpidChecker.fromSeconds(m)).isEqualTo(38L);
320 assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890");
321
322 m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("00:05:38 1234567890");
323 assertThat(m.matches()).isTrue();
324 assertThat(PpidChecker.fromDays(m)).isEqualTo(0L);
325 assertThat(PpidChecker.fromHours(m)).isEqualTo(0L);
326 assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L);
327 assertThat(PpidChecker.fromSeconds(m)).isEqualTo(38L);
328 assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890");
329
330 m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("01:05:38 1234567890");
331 assertThat(m.matches()).isTrue();
332 assertThat(PpidChecker.fromDays(m)).isEqualTo(0L);
333 assertThat(PpidChecker.fromHours(m)).isEqualTo(3600L);
334 assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L);
335 assertThat(PpidChecker.fromSeconds(m)).isEqualTo(38L);
336 assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890");
337
338 m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("02-01:05:38 1234567890");
339 assertThat(m.matches()).isTrue();
340 assertThat(PpidChecker.fromDays(m)).isEqualTo(2 * 24 * 3600L);
341 assertThat(PpidChecker.fromHours(m)).isEqualTo(3600L);
342 assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L);
343 assertThat(PpidChecker.fromSeconds(m)).isEqualTo(38L);
344 assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890");
345
346 m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("02-1:5:3 1234567890");
347 assertThat(m.matches()).isTrue();
348 assertThat(PpidChecker.fromDays(m)).isEqualTo(2 * 24 * 3600L);
349 assertThat(PpidChecker.fromHours(m)).isEqualTo(3600L);
350 assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L);
351 assertThat(PpidChecker.fromSeconds(m)).isEqualTo(3L);
352 assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890");
353 }
354
355 @Test
356 public void shouldParseBusyboxHoursEtime() {
357 Matcher m = PpidChecker.BUSYBOX_CMD_OUT_PATTERN.matcher("38 1234567890");
358 assertThat(m.matches()).isFalse();
359
360 m = PpidChecker.BUSYBOX_CMD_OUT_PATTERN.matcher("05h38 1234567890");
361 assertThat(m.matches()).isTrue();
362 assertThat(PpidChecker.fromBusyboxHours(m)).isEqualTo(3600 * 5L);
363 assertThat(PpidChecker.fromBusyboxMinutes(m)).isEqualTo(60 * 38L);
364 assertThat(PpidChecker.fromBusyboxPID(m)).isEqualTo("1234567890");
365 }
366
367 @Test
368 public void shouldHaveSystemPathToWmicOnWindows() throws Exception {
369 assumeTrue(IS_OS_WINDOWS);
370 assumeThat(System.getenv("SystemRoot"), is(notNullValue()));
371 assumeThat(System.getenv("SystemRoot"), is(not("")));
372 assumeTrue(new File(System.getenv("SystemRoot"), "System32\\Wbem").isDirectory());
373 assumeTrue(new File(System.getenv("SystemRoot"), "System32\\Wbem\\wmic.exe").isFile());
374 assertThat((Boolean) invokeMethod(PpidChecker.class, "hasWmicStandardSystemPath"))
375 .isTrue();
376 assertThat(new File(System.getenv("SystemRoot"), "System32\\Wbem\\wmic.exe"))
377 .isFile();
378 }
379
380 @Test
381 public void shouldBeTypeNull() {
382 assertThat(ProcessCheckerType.toEnum(null)).isNull();
383
384 assertThat(ProcessCheckerType.toEnum(" ")).isNull();
385
386 assertThat(ProcessCheckerType.isValid(null)).isTrue();
387 }
388
389 @Test
390 public void shouldBeException() {
391 exceptions.expect(IllegalArgumentException.class);
392 exceptions.expectMessage("unknown process checker");
393
394 assertThat(ProcessCheckerType.toEnum("anything else")).isNull();
395 }
396
397 @Test
398 public void shouldNotBeValid() {
399 assertThat(ProcessCheckerType.isValid("anything")).isFalse();
400 }
401
402 @Test
403 public void shouldBeTypePing() {
404 assertThat(ProcessCheckerType.toEnum("ping")).isEqualTo(ProcessCheckerType.PING);
405
406 assertThat(ProcessCheckerType.isValid("ping")).isTrue();
407
408 assertThat(ProcessCheckerType.PING.getType()).isEqualTo("ping");
409 }
410
411 @Test
412 public void shouldBeTypeNative() {
413 assertThat(ProcessCheckerType.toEnum("native")).isEqualTo(ProcessCheckerType.NATIVE);
414
415 assertThat(ProcessCheckerType.isValid("native")).isTrue();
416
417 assertThat(ProcessCheckerType.NATIVE.getType()).isEqualTo("native");
418 }
419
420 @Test
421 public void shouldBeTypeAll() {
422 assertThat(ProcessCheckerType.toEnum("all")).isEqualTo(ProcessCheckerType.ALL);
423
424 assertThat(ProcessCheckerType.isValid("all")).isTrue();
425
426 assertThat(ProcessCheckerType.ALL.getType()).isEqualTo("all");
427 }
428
429 private static long windowsProcessStartTime(PpidChecker checker) {
430 return checker.windows().getTime();
431 }
432 }