-1 (Please revert) On Sun, 15 Feb 2026 at 09:19, <[email protected]> wrote: > > This is an automated email from the ASF dual-hosted git repository. > > tibordigana pushed a commit to branch revert-3252-ppidchecker-processhandle > in repository https://gitbox.apache.org/repos/asf/maven-surefire.git > > commit b829dd4cad2bdcbd079747c5113ed6576109d4a5 > Author: Tibor Digana <[email protected]> > AuthorDate: Sun Feb 15 00:19:10 2026 +0100 > > Revert "Replace runing external process and parsing output with simple > Proces…" > > This reverts commit 0b190142a3df4cb3dda52825e7fedda59591cbc8. > --- > maven-surefire-common/pom.xml | 1 + > .../plugin/surefire/booterclient/Platform.java | 9 +- > .../src/site/apt/examples/shutdown.apt.vm | 6 +- > surefire-booter/pom.xml | 5 - > .../apache/maven/surefire/booter/ForkedBooter.java | 12 +- > .../apache/maven/surefire/booter/PpidChecker.java | 27 +- > .../maven/surefire/booter/ProcessChecker.java | 108 ------ > .../surefire/booter/ProcessHandleChecker.java | 238 ------------ > .../apache/maven/surefire/booter/ProcessInfo.java | 11 - > .../apache/maven/surefire/booter/SystemUtils.java | 1 - > .../maven/surefire/booter/PpidCheckerTest.java | 432 > +++++++++++++++++++++ > .../maven/surefire/booter/ProcessCheckerTest.java | 248 ------------ > .../surefire/booter/ProcessHandleCheckerTest.java | 205 ---------- > 13 files changed, 452 insertions(+), 851 deletions(-) > > diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml > index 072efcabf..a9feb6dd9 100644 > --- a/maven-surefire-common/pom.xml > +++ b/maven-surefire-common/pom.xml > @@ -172,6 +172,7 @@ > <dependency> > <groupId>commons-io</groupId> > <artifactId>commons-io</artifactId> > + <version>2.21.0</version> > <scope>test</scope> > </dependency> > </dependencies> > diff --git > a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java > > b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java > index e18803142..12d19c5e8 100644 > --- > a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java > +++ > b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java > @@ -29,7 +29,7 @@ > > /** > * Loads platform specifics. > - * TODO simplify or remove when Java 8 support is dropped > + * > * @author <a href="mailto:[email protected]">Tibor Digana (tibor17)</a> > * @since 2.20.1 > */ > @@ -80,6 +80,11 @@ public Platform > withJdkExecAttributesForTests(JdkAttributes jdk) { > } > > private static Callable<Long> pidJob() { > - return SystemUtils::pid; > + return new Callable<Long>() { > + @Override > + public Long call() throws Exception { > + return SystemUtils.pid(); > + } > + }; > } > } > diff --git a/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm > b/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm > index 93083858d..92f9ddc6c 100644 > --- a/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm > +++ b/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm > @@ -55,13 +55,9 @@ Shutdown of Forked JVM > > [] > > - If Java9 is available, the start time of the process is determined by <<< > ProcessHandle.current().info().startInstant() >>>. > - > On Unix like systems the process' uptime is determined by native command > <<< (/usr)/bin/ps -o etime= -p [PID] >>>. > > - On Windows the start time is determined using <<< powershell -command > "... Get-CimInstance Win32_Process ..." >>>. > - > - [] > + On Windows the start time is determined using <<< wmic process where > (ProcessId=[PID]) get CreationDate >>> > in the forked JVM. > > > diff --git a/surefire-booter/pom.xml b/surefire-booter/pom.xml > index 74b055a9d..c24bb7e36 100644 > --- a/surefire-booter/pom.xml > +++ b/surefire-booter/pom.xml > @@ -91,11 +91,6 @@ > <artifactId>powermock-api-mockito2</artifactId> > <scope>test</scope> > </dependency> > - <dependency> > - <groupId>commons-io</groupId> > - <artifactId>commons-io</artifactId> > - <scope>test</scope> > - </dependency> > </dependencies> > > <build> > diff --git > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java > > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java > index 5067509c3..1bcdc8b09 100644 > --- > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java > +++ > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java > @@ -215,7 +215,7 @@ private void closeForkChannel() { > } > > private PingScheduler listenToShutdownCommands(String ppid) { > - ProcessChecker ppidChecker = ProcessChecker.of(ppid); > + PpidChecker ppidChecker = ppid == null ? null : new > PpidChecker(ppid); > commandReader.addShutdownListener(createExitHandler(ppidChecker)); > AtomicBoolean pingDone = new AtomicBoolean(true); > commandReader.addNoopListener(createPingHandler(pingDone)); > @@ -280,7 +280,7 @@ public void update(Command command) { > }; > } > > - private CommandListener createExitHandler(final ProcessChecker > ppidChecker) { > + private CommandListener createExitHandler(final PpidChecker ppidChecker) > { > return new CommandListener() { > @Override > public void update(Command command) { > @@ -325,7 +325,7 @@ public void update(Command command) { > }; > } > > - private Runnable createPingJob(final AtomicBoolean pingDone, final > ProcessChecker pluginProcessChecker) { > + private Runnable createPingJob(final AtomicBoolean pingDone, final > PpidChecker pluginProcessChecker) { > return new Runnable() { > @Override > public void run() { > @@ -515,7 +515,7 @@ private static void run(ForkedBooter booter, String[] > args) { > } > } > > - private static boolean canUseNewPingMechanism(ProcessChecker > pluginProcessChecker) { > + private static boolean canUseNewPingMechanism(PpidChecker > pluginProcessChecker) { > return pluginProcessChecker != null && pluginProcessChecker.canUse(); > } > > @@ -553,12 +553,12 @@ private static boolean isDebugging() { > private static class PingScheduler { > private final ScheduledExecutorService pingScheduler; > private final ScheduledExecutorService processCheckerScheduler; > - private final ProcessChecker processChecker; > + private final PpidChecker processChecker; > > PingScheduler( > ScheduledExecutorService pingScheduler, > ScheduledExecutorService processCheckerScheduler, > - ProcessChecker processChecker) { > + PpidChecker processChecker) { > this.pingScheduler = pingScheduler; > this.processCheckerScheduler = processCheckerScheduler; > this.processChecker = processChecker; > diff --git > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java > > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java > index bfcc70d18..b8891e822 100644 > --- > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java > +++ > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java > @@ -57,18 +57,11 @@ > > /** > * Recognizes PID of Plugin process and determines lifetime. > - * <p> > - * This implementation uses native commands ({@code ps} on Unix, {@code > powershell} on Windows) > - * to check the parent process status. On Java 9+, consider using {@code > ProcessHandleChecker} > - * instead, which uses the Java {@code ProcessHandle} API and doesn't > require spawning external processes. > * > * @author <a href="mailto:[email protected]">Tibor Digana (tibor17)</a> > * @since 2.20.1 > - * @see ProcessChecker > - * @deprecated Use {@code ProcessHandleChecker} via {@link > ProcessChecker#of(String)} instead > */ > -@Deprecated > -final class PpidChecker implements ProcessChecker { > +final class PpidChecker { > private static final long MINUTES_TO_MILLIS = 60L * 1000L; > // 25 chars > https://superuser.com/questions/937380/get-creation-time-of-file-in-milliseconds/937401#937401 > private static final int WMIC_CREATION_DATE_VALUE_LENGTH = 25; > @@ -102,8 +95,7 @@ final class PpidChecker implements ProcessChecker { > this.ppid = ppid; > } > > - @Override > - public boolean canUse() { > + boolean canUse() { > if (isStopped()) { > return false; > } > @@ -119,8 +111,7 @@ public boolean canUse() { > * or this object has been {@link > #destroyActiveCommands() destroyed} > * @throws NullPointerException if extracted e-time is null > */ > - @Override > - public boolean isProcessAlive() { > + boolean isProcessAlive() { > if (!canUse()) { > throw new IllegalStateException("irrelevant to call > isProcessAlive()"); > } > @@ -235,16 +226,14 @@ ProcessInfo consumeLine(String line, ProcessInfo > previousProcessInfo) throws Exc > return reader.execute(psPath + "powershell", "-NoProfile", > "-NonInteractive", "-Command", psCommand); > } > > - @Override > - public void destroyActiveCommands() { > + void destroyActiveCommands() { > stopped = true; > for (Process p = destroyableCommands.poll(); p != null; p = > destroyableCommands.poll()) { > p.destroy(); > } > } > > - @Override > - public boolean isStopped() { > + boolean isStopped() { > return stopped; > } > > @@ -336,16 +325,10 @@ private static SimpleDateFormat > createWindowsCreationDateFormat() { > return formatter; > } > > - @Override > public void stop() { > stopped = true; > } > > - @Override > - public ProcessInfo processInfo() { > - return parentProcessInfo; > - } > - > /** > * Reads standard output from {@link Process}. > * <br> > diff --git > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessChecker.java > > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessChecker.java > deleted file mode 100644 > index ef495eb53..000000000 > --- > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessChecker.java > +++ /dev/null > @@ -1,108 +0,0 @@ > -/* > - * Licensed to the Apache Software Foundation (ASF) under one > - * or more contributor license agreements. See the NOTICE file > - * distributed with this work for additional information > - * regarding copyright ownership. The ASF licenses this file > - * to you under the Apache License, Version 2.0 (the > - * "License"); you may not use this file except in compliance > - * with the License. You may obtain a copy of the License at > - * > - * http://www.apache.org/licenses/LICENSE-2.0 > - * > - * Unless required by applicable law or agreed to in writing, > - * software distributed under the License is distributed on an > - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > - * KIND, either express or implied. See the License for the > - * specific language governing permissions and limitations > - * under the License. > - */ > -package org.apache.maven.surefire.booter; > - > -/** > - * Interface for checking if a process (typically the parent Maven plugin) > is still alive. > - * <p> > - * Implementations allow the forked JVM to detect when its parent Maven > process > - * has terminated, enabling cleanup and preventing orphaned processes. > - * > - * @since 3.5.5 > - */ > -public interface ProcessChecker { > - > - /** > - * Creates the appropriate {@link ProcessChecker} implementation for the > given parent PID. > - * <p> > - * On Java 9+, uses {@code ProcessHandleChecker} which leverages the > {@code ProcessHandle} API. > - * On Java 8, falls back to {@link PpidChecker} which uses native > commands. > - * > - * @param ppid the parent process ID as a string, or {@code null} > - * @return a new checker instance, or {@code null} if ppid is {@code > null} > - */ > - static ProcessChecker of(String ppid) { > - if (ppid == null) { > - return null; > - } > - if (ProcessHandleChecker.isAvailable()) { > - return new ProcessHandleChecker(ppid); > - } > - return new PpidChecker(ppid); > - } > - > - /** > - * Returns whether the ProcessHandle API is available in the current JVM. > - * > - * @return {@code true} if running on Java 9+ with ProcessHandle > available > - */ > - static boolean isProcessHandleSupported() { > - return ProcessHandleChecker.isAvailable(); > - } > - > - /** > - * Checks whether this checker can be used to monitor the process. > - * <p> > - * This method must return {@code true} before {@link #isProcessAlive()} > can be called. > - * @deprecated with using ProcessHandleChecker on Java 9+, this method > will always return {@code true} and can be removed in a future release. > - * @return {@code true} if the checker is operational and can monitor > the process > - */ > - @Deprecated > - boolean canUse(); > - > - /** > - * Checks if the process is still alive. > - * <p> > - * This method can only be called after {@link #canUse()} has returned > {@code true}. > - * > - * @return {@code true} if the process is still running; {@code false} > if it has terminated > - * or if the PID has been reused by a different process > - * @throws IllegalStateException if {@link #canUse()} returns {@code > false} or if the checker > - * has been stopped > - */ > - boolean isProcessAlive(); > - > - /** > - * Stops the checker and releases any resources. > - * <p> > - * After calling this method, {@link #canUse()} will return {@code > false}. > - */ > - void stop(); > - > - /** > - * Destroys any active commands or subprocesses used by this checker. > - * <p> > - * This is called during shutdown to ensure clean termination. > - */ > - void destroyActiveCommands(); > - > - /** > - * Checks if the checker has been stopped. > - * > - * @return {@code true} if {@link #stop()} or {@link > #destroyActiveCommands()} has been called > - */ > - boolean isStopped(); > - > - /** > - * Returns information about the process being checked. > - * > - * @return the process information, or {@code null} if not yet > initialized > - */ > - ProcessInfo processInfo(); > -} > diff --git > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessHandleChecker.java > > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessHandleChecker.java > deleted file mode 100644 > index a97e6ace4..000000000 > --- > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessHandleChecker.java > +++ /dev/null > @@ -1,238 +0,0 @@ > -/* > - * Licensed to the Apache Software Foundation (ASF) under one > - * or more contributor license agreements. See the NOTICE file > - * distributed with this work for additional information > - * regarding copyright ownership. The ASF licenses this file > - * to you under the Apache License, Version 2.0 (the > - * "License"); you may not use this file except in compliance > - * with the License. You may obtain a copy of the License at > - * > - * http://www.apache.org/licenses/LICENSE-2.0 > - * > - * Unless required by applicable law or agreed to in writing, > - * software distributed under the License is distributed on an > - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > - * KIND, either express or implied. See the License for the > - * specific language governing permissions and limitations > - * under the License. > - */ > -package org.apache.maven.surefire.booter; > - > -import javax.annotation.Nonnull; > - > -import java.lang.reflect.Method; > -import java.util.Optional; > - > -import static > org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray; > -import static > org.apache.maven.surefire.api.util.ReflectionUtils.tryGetMethod; > -import static > org.apache.maven.surefire.api.util.ReflectionUtils.tryLoadClass; > - > -/** > - * Checks if a process is alive using the ProcessHandle API via reflection. > - * <p> > - * This implementation uses reflection to access the Java 9+ {@code > ProcessHandle} API, > - * allowing the class to compile on Java 8 while functioning on Java 9+. > - * <p> > - * The checker detects two scenarios indicating the process is no longer > available: > - * <ol> > - * <li>The process has terminated ({@code ProcessHandle.isAlive()} returns > {@code false})</li> > - * <li>The PID has been reused by the OS for a new process (start time > differs from initial)</li> > - * </ol> > - * > - * @since 3.5.5 > - */ > -final class ProcessHandleChecker implements ProcessChecker { > - > - /** Whether ProcessHandle API is available and reflection setup > succeeded */ > - private static final boolean AVAILABLE; > - > - // Method references for ProcessHandle > - private static final Method PROCESS_HANDLE_OF; // ProcessHandle.of(long) > -> Optional<ProcessHandle> > - private static final Method PROCESS_HANDLE_IS_ALIVE; // > ProcessHandle.isAlive() -> boolean > - private static final Method PROCESS_HANDLE_INFO; // ProcessHandle.info() > -> ProcessHandle.Info > - > - // Method references for ProcessHandle.Info > - private static final Method INFO_START_INSTANT; // > ProcessHandle.Info.startInstant() -> Optional<Instant> > - > - // Method reference for Instant > - private static final Method INSTANT_TO_EPOCH_MILLI; // > Instant.toEpochMilli() -> long > - > - static { > - ClassLoader classLoader = > Thread.currentThread().getContextClassLoader(); > - > - // Load classes using ReflectionUtils > - Class<?> processHandleClass = tryLoadClass(classLoader, > "java.lang.ProcessHandle"); > - Class<?> processHandleInfoClass = tryLoadClass(classLoader, > "java.lang.ProcessHandle$Info"); > - Class<?> optionalClass = tryLoadClass(classLoader, > "java.util.Optional"); > - Class<?> instantClass = tryLoadClass(classLoader, > "java.time.Instant"); > - > - Method processHandleOf = null; > - Method processHandleIsAlive = null; > - Method processHandleInfo = null; > - Method infoStartInstant = null; > - Method optionalIsPresent = null; > - Method optionalGet = null; > - Method optionalOrElse = null; > - Method instantToEpochMilli = null; > - > - if (processHandleClass != null && processHandleInfoClass != null && > optionalClass != null) { > - // ProcessHandle methods > - processHandleOf = tryGetMethod(processHandleClass, "of", > long.class); > - processHandleIsAlive = tryGetMethod(processHandleClass, > "isAlive"); > - processHandleInfo = tryGetMethod(processHandleClass, "info"); > - > - // ProcessHandle.Info methods > - infoStartInstant = tryGetMethod(processHandleInfoClass, > "startInstant"); > - > - // Optional methods > - optionalIsPresent = tryGetMethod(optionalClass, "isPresent"); > - optionalGet = tryGetMethod(optionalClass, "get"); > - optionalOrElse = tryGetMethod(optionalClass, "orElse", > Object.class); > - > - // Instant methods (for processInfo) > - if (instantClass != null) { > - instantToEpochMilli = tryGetMethod(instantClass, > "toEpochMilli"); > - } > - } > - > - // All methods must be available for ProcessHandle API to be usable > - AVAILABLE = processHandleOf != null > - && processHandleIsAlive != null > - && processHandleInfo != null > - && infoStartInstant != null > - && optionalIsPresent != null > - && optionalGet != null > - && optionalOrElse != null; > - > - PROCESS_HANDLE_OF = processHandleOf; > - PROCESS_HANDLE_IS_ALIVE = processHandleIsAlive; > - PROCESS_HANDLE_INFO = processHandleInfo; > - INFO_START_INSTANT = infoStartInstant; > - INSTANT_TO_EPOCH_MILLI = instantToEpochMilli; > - } > - > - private final long pid; > - private final Object processHandle; // ProcessHandle (stored as Object) > - private volatile Object initialStartInstant; // Instant (stored as > Object) > - private volatile boolean stopped; > - > - /** > - * Creates a new checker for the given process ID. > - * > - * @param pid the process ID as a string > - * @throws NumberFormatException if pid is not a valid long > - */ > - ProcessHandleChecker(@Nonnull String pid) { > - this.pid = Long.parseLong(pid); > - try { > - Optional<?> optionalObject = (Optional<?>) > PROCESS_HANDLE_OF.invoke(null, this.pid); > - processHandle = optionalObject.orElse(null); > - initialStartInstant = getInitialStartInstant(); > - } catch (Exception e) { > - throw new IllegalStateException("Failed to initialize > ProcessHandleChecker for PID " + pid, e); > - } > - } > - > - /** > - * Returns whether the ProcessHandle API is available for use. > - * This is a static check that can be used by the factory. > - * > - * @return true if ProcessHandle API is available (Java 9+) > - */ > - static boolean isAvailable() { > - return AVAILABLE; > - } > - > - @Override > - public boolean canUse() { > - return (AVAILABLE && !stopped); > - } > - > - /** > - * {@inheritDoc} > - * <p> > - * This implementation checks both that the process is alive and that > it's the same process > - * that was originally identified (by comparing start times to detect > PID reuse). > - */ > - @Override > - public boolean isProcessAlive() { > - if (!canUse()) { > - throw new IllegalStateException("irrelevant to call > isProcessAlive()"); > - } > - > - try { > - // Check if process is still running: processHandle.isAlive() > - boolean isAlive = invokeMethodWithArray(processHandle, > PROCESS_HANDLE_IS_ALIVE); > - if (!isAlive) { > - return false; > - } > - > - // Verify it's the same process (not a reused PID) > - if (initialStartInstant != null) { > - // processHandle.info().startInstant() > - Object info = invokeMethodWithArray(processHandle, > PROCESS_HANDLE_INFO); > - Optional<?> optionalInstant = invokeMethodWithArray(info, > INFO_START_INSTANT); > - > - if (optionalInstant.isPresent()) { > - Object currentStartInstant = optionalInstant.get(); > - // PID was reused for a different process > - return currentStartInstant.equals(initialStartInstant); > - } > - } > - > - return true; > - } catch (RuntimeException e) { > - // Reflection failed during runtime - treat as process not alive > - return false; > - } > - } > - > - private Object getInitialStartInstant() { > - try { > - Object info = invokeMethodWithArray(processHandle, > PROCESS_HANDLE_INFO); > - Optional<?> optionalInstant = invokeMethodWithArray(info, > INFO_START_INSTANT); > - return optionalInstant.orElse(null); > - } catch (RuntimeException e) { > - return null; > - } > - } > - > - @Override > - public void destroyActiveCommands() { > - stopped = true; > - // No subprocess to destroy - ProcessHandle doesn't spawn processes > - } > - > - @Override > - public boolean isStopped() { > - return stopped; > - } > - > - @Override > - public void stop() { > - stopped = true; > - } > - > - @Override > - public ProcessInfo processInfo() { > - Object startInstant = getInitialStartInstant(); > - if (startInstant == null || INSTANT_TO_EPOCH_MILLI == null) { > - return null; > - } > - try { > - long startTimeMillis = invokeMethodWithArray(startInstant, > INSTANT_TO_EPOCH_MILLI); > - return ProcessInfo.processHandleInfo(String.valueOf(pid), > startTimeMillis); > - } catch (RuntimeException e) { > - return null; > - } > - } > - > - @Override > - public String toString() { > - String args = "pid=" + pid + ", stopped=" + stopped + ", hasHandle=" > + (processHandle != null); > - if (initialStartInstant != null) { > - args += ", startInstant=" + initialStartInstant; > - } > - return "ProcessHandleChecker{" + args + "}"; > - } > -} > diff --git > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java > > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java > index 630c7dd5b..771445965 100644 > --- > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java > +++ > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java > @@ -48,17 +48,6 @@ final class ProcessInfo { > return new ProcessInfo(pid, startTimestamp); > } > > - /** > - * Creates process info from ProcessHandle API data. > - * > - * @param pid the process ID > - * @param startTimeMillis the process start time in epoch milliseconds > - * @return a new ProcessInfo instance > - */ > - static @Nonnull ProcessInfo processHandleInfo(String pid, long > startTimeMillis) { > - return new ProcessInfo(pid, startTimeMillis); > - } > - > private final String pid; > private final long time; > > diff --git > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SystemUtils.java > > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SystemUtils.java > index 35f5cf75e..3f7b4aa5b 100644 > --- > a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SystemUtils.java > +++ > b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SystemUtils.java > @@ -172,7 +172,6 @@ public static ClassLoader platformClassLoader() { > return null; > } > > - // TODO simplify or remove when Java 8 support is dropped > public static Long pid() { > if (isBuiltInJava9AtLeast()) { > Long pid = pidOnJava9(); > diff --git > a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java > > b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java > new file mode 100644 > index 000000000..5b05d1a46 > --- /dev/null > +++ > b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java > @@ -0,0 +1,432 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one > + * or more contributor license agreements. See the NOTICE file > + * distributed with this work for additional information > + * regarding copyright ownership. The ASF licenses this file > + * to you under the Apache License, Version 2.0 (the > + * "License"); you may not use this file except in compliance > + * with the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, > + * software distributed under the License is distributed on an > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > + * KIND, either express or implied. See the License for the > + * specific language governing permissions and limitations > + * under the License. > + */ > +package org.apache.maven.surefire.booter; > + > +import javax.annotation.Nonnull; > + > +import java.io.File; > +import java.io.IOException; > +import java.io.InterruptedIOException; > +import java.lang.management.ManagementFactory; > +import java.util.Random; > +import java.util.regex.Matcher; > + > +import org.apache.maven.surefire.api.booter.DumpErrorSingleton; > +import org.junit.After; > +import org.junit.Before; > +import org.junit.Rule; > +import org.junit.Test; > +import org.junit.rules.ExpectedException; > +import org.junit.rules.TemporaryFolder; > + > +import static java.nio.charset.StandardCharsets.US_ASCII; > +import static java.nio.file.Files.readAllBytes; > +import static java.util.concurrent.TimeUnit.SECONDS; > +import static org.apache.maven.surefire.booter.ProcessInfo.unixProcessInfo; > +import static > org.apache.maven.surefire.booter.ProcessInfo.windowsProcessInfo; > +import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_UNIX; > +import static > org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS; > +import static org.assertj.core.api.Assertions.assertThat; > +import static org.hamcrest.CoreMatchers.is; > +import static org.hamcrest.CoreMatchers.not; > +import static org.hamcrest.CoreMatchers.notNullValue; > +import static org.junit.Assert.fail; > +import static org.junit.Assume.assumeThat; > +import static org.junit.Assume.assumeTrue; > +import static org.powermock.reflect.Whitebox.invokeMethod; > +import static org.powermock.reflect.Whitebox.setInternalState; > + > +/** > + * Testing {@link PpidChecker} on a platform. > + * > + * @author <a href="mailto:[email protected]">Tibor Digana (tibor17)</a> > + * @since 2.20.1 > + */ > +@SuppressWarnings("checkstyle:magicnumber") > +public class PpidCheckerTest { > + private static final Random RND = new Random(); > + > + @Rule > + public final ExpectedException exceptions = ExpectedException.none(); > + > + @Rule > + public final TemporaryFolder tempFolder = new TemporaryFolder(); > + > + private File reportsDir; > + private String dumpFileName; > + > + @Before > + public void initTmpFile() { > + reportsDir = tempFolder.getRoot(); > + dumpFileName = "surefire-" + RND.nextLong(); > + } > + > + @After > + public void deleteTmpFiles() { > + tempFolder.delete(); > + } > + > + @Test > + public void canExecuteUnixPs() { > + assumeTrue(IS_OS_UNIX); > + assertThat(PpidChecker.canExecuteUnixPs()) > + .as("Surefire should be tested on real box OS, e.g. Ubuntu > or FreeBSD.") > + .isTrue(); > + } > + > + @Test > + public void shouldHavePidAtBegin() { > + String expectedPid = > + > ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim(); > + > + PpidChecker checker = new PpidChecker(expectedPid); > + ProcessInfo processInfo = IS_OS_UNIX ? checker.unix() : > checker.windows(); > + > + assertThat(processInfo).isNotNull(); > + > + assertThat(checker.canUse()).isTrue(); > + > + assertThat(checker.isProcessAlive()).isTrue(); > + > + assertThat(processInfo.getPID()).isEqualTo(expectedPid); > + > + assertThat(processInfo.getTime()).isGreaterThan(0L); > + } > + > + @Test > + public void shouldHavePid() throws Exception { > + String expectedPid = > + > ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim(); > + > + PpidChecker checker = new PpidChecker(expectedPid); > + setInternalState( > + checker, > + "parentProcessInfo", > + IS_OS_UNIX > + ? unixProcessInfo(expectedPid, 0L) > + : windowsProcessInfo(expectedPid, > windowsProcessStartTime(checker))); > + > + // the etime in Unix is measured in seconds. So let's wait 1 s at > least. > + SECONDS.sleep(1L); > + > + ProcessInfo processInfo = IS_OS_UNIX ? checker.unix() : > checker.windows(); > + > + assertThat(processInfo).isNotNull(); > + > + assertThat(checker.canUse()).isTrue(); > + > + assertThat(checker.isProcessAlive()).isTrue(); > + > + assertThat(processInfo.getPID()).isEqualTo(expectedPid); > + > + assertThat(processInfo.getTime()).isGreaterThan(0L); > + > + assertThat(checker.toString()) > + .contains("ppid=" + expectedPid) > + .contains("stopped=false") > + .contains("invalid=false") > + .contains("error=false"); > + > + checker.destroyActiveCommands(); > + assertThat(checker.canUse()).isFalse(); > + assertThat((boolean) invokeMethod(checker, "isStopped")).isTrue(); > + } > + > + @Test > + public void shouldBeStopped() { > + PpidChecker checker = new PpidChecker("0"); > + checker.stop(); > + > + assertThat(checker.canUse()).isFalse(); > + > + exceptions.expect(IllegalStateException.class); > + exceptions.expectMessage("irrelevant to call isProcessAlive()"); > + > + checker.isProcessAlive(); > + > + fail("this test should throw exception"); > + } > + > + @Test > + public void shouldBeStoppedCheckerWithError() throws Exception { > + String expectedPid = > + > ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim(); > + DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName); > + > + PpidChecker checker = new PpidChecker(expectedPid); > + checker.stop(); > + > + ProcessInfo processInfo = IS_OS_UNIX ? checker.unix() : > checker.windows(); > + assertThat(processInfo.isError()).isTrue(); > + > + String error = new String(readAllBytes(new File(reportsDir, > dumpFileName + ".dump").toPath())); > + > + assertThat(error).contains("<<exit>> <<0>>").contains("<<stopped>> > <<true>>"); > + } > + > + @Test > + public void shouldBeEmptyDump() throws Exception { > + String expectedPid = > + > ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim(); > + DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName); > + > + PpidChecker checker = new PpidChecker(expectedPid); > + > + try { > + Thread.currentThread().interrupt(); > + > + ProcessInfo processInfo = IS_OS_UNIX ? checker.unix() : > checker.windows(); > + //noinspection ResultOfMethodCallIgnored > + Thread.interrupted(); > + assertThat(processInfo.isError()).isTrue(); > + > + File dumpFile = new File(reportsDir, dumpFileName + ".dump"); > + if (dumpFile.exists()) { > + String error = new String(readAllBytes(dumpFile.toPath())); > + > + assertThat(error).contains("<<exit>>").contains("<<stopped>> > <<false>>"); > + } > + } finally { > + //noinspection ResultOfMethodCallIgnored > + Thread.interrupted(); > + } > + } > + > + @Test > + public void shouldStartedProcessThrowInterruptedException() throws > Exception { > + String expectedPid = > + > ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim(); > + DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName); > + > + PpidChecker checker = new PpidChecker(expectedPid); > + > + PpidChecker.ProcessInfoConsumer consumer = checker.new > ProcessInfoConsumer(US_ASCII.name()) { > + @Nonnull > + @Override > + ProcessInfo consumeLine(String line, ProcessInfo > previousProcessInfo) throws Exception { > + throw new InterruptedException(); > + } > + }; > + > + String[] cmd = > + IS_OS_WINDOWS ? new String[] {"CMD", "/A", "/X", "/C", > "dir"} : new String[] {"/bin/sh", "-c", "ls"}; > + > + assertThat(consumer.execute(cmd).isError()).isTrue(); > + assertThat(new File(reportsDir, dumpFileName + > ".dump")).doesNotExist(); > + } > + > + @Test > + public void shouldStartedProcessThrowInterruptedIOException() throws > Exception { > + String expectedPid = > + > ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim(); > + DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName); > + > + PpidChecker checker = new PpidChecker(expectedPid); > + > + PpidChecker.ProcessInfoConsumer consumer = checker.new > ProcessInfoConsumer(US_ASCII.name()) { > + @Nonnull > + @Override > + ProcessInfo consumeLine(String line, ProcessInfo > previousProcessInfo) throws Exception { > + throw new InterruptedIOException(); > + } > + }; > + > + String[] cmd = > + IS_OS_WINDOWS ? new String[] {"CMD", "/A", "/X", "/C", > "dir"} : new String[] {"/bin/sh", "-c", "ls"}; > + > + assertThat(consumer.execute(cmd).isError()).isTrue(); > + assertThat(new File(reportsDir, dumpFileName + > ".dump")).doesNotExist(); > + } > + > + @Test > + public void shouldStartedProcessThrowIOException() throws Exception { > + String expectedPid = > + > ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim(); > + DumpErrorSingleton.getSingleton().init(reportsDir, dumpFileName); > + > + PpidChecker checker = new PpidChecker(expectedPid); > + > + PpidChecker.ProcessInfoConsumer consumer = checker.new > ProcessInfoConsumer(US_ASCII.name()) { > + @Nonnull > + @Override > + ProcessInfo consumeLine(String line, ProcessInfo > previousProcessInfo) throws Exception { > + throw new IOException("wrong command"); > + } > + }; > + > + String[] cmd = > + IS_OS_WINDOWS ? new String[] {"CMD", "/A", "/X", "/C", > "dir"} : new String[] {"/bin/sh", "-c", "ls"}; > + > + assertThat(consumer.execute(cmd).isError()).isTrue(); > + > + File dumpFile = new File(reportsDir, dumpFileName + ".dump"); > + > + String error = new String(readAllBytes(dumpFile.toPath())); > + > + > assertThat(error).contains(IOException.class.getName()).contains("wrong > command"); > + } > + > + @Test > + public void shouldNotFindSuchPID() { > + PpidChecker checker = new PpidChecker("1000000"); > + setInternalState(checker, "parentProcessInfo", > ProcessInfo.ERR_PROCESS_INFO); > + > + assertThat(checker.canUse()).isFalse(); > + > + exceptions.expect(IllegalStateException.class); > + exceptions.expectMessage("irrelevant to call isProcessAlive()"); > + > + checker.isProcessAlive(); > + > + fail("this test should throw exception"); > + } > + > + @Test > + public void shouldNotBeAlive() { > + PpidChecker checker = new PpidChecker("1000000"); > + > + assertThat(checker.canUse()).isTrue(); > + > + assertThat(checker.isProcessAlive()).isFalse(); > + } > + > + @Test > + public void shouldParseEtime() { > + Matcher m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("38 > 1234567890"); > + assertThat(m.matches()).isFalse(); > + > + m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("05:38 1234567890"); > + assertThat(m.matches()).isTrue(); > + assertThat(PpidChecker.fromDays(m)).isEqualTo(0L); > + assertThat(PpidChecker.fromHours(m)).isEqualTo(0L); > + assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L); > + assertThat(PpidChecker.fromSeconds(m)).isEqualTo(38L); > + assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890"); > + > + m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("00:05:38 1234567890"); > + assertThat(m.matches()).isTrue(); > + assertThat(PpidChecker.fromDays(m)).isEqualTo(0L); > + assertThat(PpidChecker.fromHours(m)).isEqualTo(0L); > + assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L); > + assertThat(PpidChecker.fromSeconds(m)).isEqualTo(38L); > + assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890"); > + > + m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("01:05:38 1234567890"); > + assertThat(m.matches()).isTrue(); > + assertThat(PpidChecker.fromDays(m)).isEqualTo(0L); > + assertThat(PpidChecker.fromHours(m)).isEqualTo(3600L); > + assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L); > + assertThat(PpidChecker.fromSeconds(m)).isEqualTo(38L); > + assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890"); > + > + m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("02-01:05:38 > 1234567890"); > + assertThat(m.matches()).isTrue(); > + assertThat(PpidChecker.fromDays(m)).isEqualTo(2 * 24 * 3600L); > + assertThat(PpidChecker.fromHours(m)).isEqualTo(3600L); > + assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L); > + assertThat(PpidChecker.fromSeconds(m)).isEqualTo(38L); > + assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890"); > + > + m = PpidChecker.UNIX_CMD_OUT_PATTERN.matcher("02-1:5:3 1234567890"); > + assertThat(m.matches()).isTrue(); > + assertThat(PpidChecker.fromDays(m)).isEqualTo(2 * 24 * 3600L); > + assertThat(PpidChecker.fromHours(m)).isEqualTo(3600L); > + assertThat(PpidChecker.fromMinutes(m)).isEqualTo(300L); > + assertThat(PpidChecker.fromSeconds(m)).isEqualTo(3L); > + assertThat(PpidChecker.fromPID(m)).isEqualTo("1234567890"); > + } > + > + @Test > + public void shouldParseBusyboxHoursEtime() { > + Matcher m = PpidChecker.BUSYBOX_CMD_OUT_PATTERN.matcher("38 > 1234567890"); > + assertThat(m.matches()).isFalse(); > + > + m = PpidChecker.BUSYBOX_CMD_OUT_PATTERN.matcher("05h38 1234567890"); > + assertThat(m.matches()).isTrue(); > + assertThat(PpidChecker.fromBusyboxHours(m)).isEqualTo(3600 * 5L); > + assertThat(PpidChecker.fromBusyboxMinutes(m)).isEqualTo(60 * 38L); > + assertThat(PpidChecker.fromBusyboxPID(m)).isEqualTo("1234567890"); > + } > + > + @Test > + public void shouldHaveSystemPathToPowerShellOnWindows() throws Exception > { > + assumeTrue(IS_OS_WINDOWS); > + assumeThat(System.getenv("SystemRoot"), is(notNullValue())); > + assumeThat(System.getenv("SystemRoot"), is(not(""))); > + assumeTrue(new File(System.getenv("SystemRoot"), > "System32\\WindowsPowerShell\\v1.0").isDirectory()); > + assumeTrue(new File(System.getenv("SystemRoot"), > "System32\\WindowsPowerShell\\v1.0\\powershell.exe").isFile()); > + assertThat((Boolean) invokeMethod(PpidChecker.class, > "hasPowerShellStandardSystemPath")) > + .isTrue(); > + assertThat(new File(System.getenv("SystemRoot"), > "System32\\WindowsPowerShell\\v1.0\\powershell.exe")) > + .isFile(); > + } > + > + @Test > + public void shouldBeTypeNull() { > + assertThat(ProcessCheckerType.toEnum(null)).isNull(); > + > + assertThat(ProcessCheckerType.toEnum(" ")).isNull(); > + > + assertThat(ProcessCheckerType.isValid(null)).isTrue(); > + } > + > + @Test > + public void shouldBeException() { > + exceptions.expect(IllegalArgumentException.class); > + exceptions.expectMessage("unknown process checker"); > + > + assertThat(ProcessCheckerType.toEnum("anything else")).isNull(); > + } > + > + @Test > + public void shouldNotBeValid() { > + assertThat(ProcessCheckerType.isValid("anything")).isFalse(); > + } > + > + @Test > + public void shouldBeTypePing() { > + > assertThat(ProcessCheckerType.toEnum("ping")).isEqualTo(ProcessCheckerType.PING); > + > + assertThat(ProcessCheckerType.isValid("ping")).isTrue(); > + > + assertThat(ProcessCheckerType.PING.getType()).isEqualTo("ping"); > + } > + > + @Test > + public void shouldBeTypeNative() { > + > assertThat(ProcessCheckerType.toEnum("native")).isEqualTo(ProcessCheckerType.NATIVE); > + > + assertThat(ProcessCheckerType.isValid("native")).isTrue(); > + > + assertThat(ProcessCheckerType.NATIVE.getType()).isEqualTo("native"); > + } > + > + @Test > + public void shouldBeTypeAll() { > + > assertThat(ProcessCheckerType.toEnum("all")).isEqualTo(ProcessCheckerType.ALL); > + > + assertThat(ProcessCheckerType.isValid("all")).isTrue(); > + > + assertThat(ProcessCheckerType.ALL.getType()).isEqualTo("all"); > + } > + > + private static long windowsProcessStartTime(PpidChecker checker) { > + return checker.windows().getTime(); > + } > +} > diff --git > a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ProcessCheckerTest.java > > b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ProcessCheckerTest.java > deleted file mode 100644 > index ac5883d17..000000000 > --- > a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ProcessCheckerTest.java > +++ /dev/null > @@ -1,248 +0,0 @@ > -/* > - * Licensed to the Apache Software Foundation (ASF) under one > - * or more contributor license agreements. See the NOTICE file > - * distributed with this work for additional information > - * regarding copyright ownership. The ASF licenses this file > - * to you under the Apache License, Version 2.0 (the > - * "License"); you may not use this file except in compliance > - * with the License. You may obtain a copy of the License at > - * > - * http://www.apache.org/licenses/LICENSE-2.0 > - * > - * Unless required by applicable law or agreed to in writing, > - * software distributed under the License is distributed on an > - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > - * KIND, either express or implied. See the License for the > - * specific language governing permissions and limitations > - * under the License. > - */ > -package org.apache.maven.surefire.booter; > - > -import java.lang.management.ManagementFactory; > - > -import org.junit.Assume; > -import org.junit.Rule; > -import org.junit.Test; > -import org.junit.rules.ExpectedException; > - > -import static org.assertj.core.api.Assertions.assertThat; > -import static org.assertj.core.api.Assertions.assertThatThrownBy; > -import static org.junit.Assert.fail; > - > -/** > - * Testing {@link ProcessChecker} via {@link ProcessChecker#of(String)}. > - * > - * @since 2.20.1 > - */ > -@SuppressWarnings("checkstyle:magicnumber") > -public class ProcessCheckerTest { > - > - @Rule > - public final ExpectedException exceptions = ExpectedException.none(); > - > - @Test > - public void shouldHavePidAtBegin() { > - String expectedPid = > - > ManagementFactory.getRuntimeMXBean().getName().split("@")[0].trim(); > - > - ProcessChecker checker = ProcessChecker.of(expectedPid); > - > - assertThat(checker).isNotNull(); > - > - assertThat(checker.canUse()).isTrue(); > - > - assertThat(checker.isProcessAlive()).isTrue(); > - > - ProcessInfo processInfo = checker.processInfo(); > - assertThat(processInfo).isNotNull(); > - assertThat(processInfo.getPID()).isEqualTo(expectedPid); > - assertThat(processInfo.getTime()).isGreaterThan(0L); > - } > - > - @Test > - public void shouldBeStopped() { > - ProcessChecker checker = ProcessChecker.of("0"); > - checker.stop(); > - > - assertThat(checker.canUse()).isFalse(); > - > - exceptions.expect(IllegalStateException.class); > - exceptions.expectMessage("irrelevant to call isProcessAlive()"); > - > - checker.isProcessAlive(); > - > - fail("this test should throw exception"); > - } > - > - @Test > - public void exceptionCallIsProcessAlive() { > - // FIXME DisabledOnJre when we migrate to junit5 and run on unix too > - // winddows java 8 must depends on wwmc something available > - double v = > Double.parseDouble(System.getProperty("java.specification.version")); > - Assume.assumeTrue(v >= 9.0); > - ProcessChecker checker = > ProcessChecker.of(Long.toString(Integer.MAX_VALUE)); > - checker.stop(); > - > assertThatThrownBy(checker::isProcessAlive).isInstanceOf(IllegalStateException.class); > - } > - > - @Test > - public void shouldReturnNullForNullPpid() { > - ProcessChecker checker = ProcessChecker.of(null); > - assertThat(checker).isNull(); > - } > - > - @Test > - public void shouldBeTypeNull() { > - assertThat(ProcessCheckerType.toEnum(null)).isNull(); > - > - assertThat(ProcessCheckerType.toEnum(" ")).isNull(); > - > - assertThat(ProcessCheckerType.isValid(null)).isTrue(); > - } > - > - @Test > - public void shouldBeException() { > - exceptions.expect(IllegalArgumentException.class); > - exceptions.expectMessage("unknown process checker"); > - > - assertThat(ProcessCheckerType.toEnum("anything else")).isNull(); > - } > - > - @Test > - public void shouldNotBeValid() { > - assertThat(ProcessCheckerType.isValid("anything")).isFalse(); > - } > - > - @Test > - public void shouldBeTypePing() { > - > assertThat(ProcessCheckerType.toEnum("ping")).isEqualTo(ProcessCheckerType.PING); > - > - assertThat(ProcessCheckerType.isValid("ping")).isTrue(); > - > - assertThat(ProcessCheckerType.PING.getType()).isEqualTo("ping"); > - } > - > - @Test > - public void shouldBeTypeNative() { > - > assertThat(ProcessCheckerType.toEnum("native")).isEqualTo(ProcessCheckerType.NATIVE); > - > - assertThat(ProcessCheckerType.isValid("native")).isTrue(); > - > - assertThat(ProcessCheckerType.NATIVE.getType()).isEqualTo("native"); > - } > - > - @Test > - public void shouldBeTypeAll() { > - > assertThat(ProcessCheckerType.toEnum("all")).isEqualTo(ProcessCheckerType.ALL); > - > - assertThat(ProcessCheckerType.isValid("all")).isTrue(); > - > - assertThat(ProcessCheckerType.ALL.getType()).isEqualTo("all"); > - } > - > - @Test > - public void shouldCreateCheckerForCurrentProcess() { > - // Get current process PID using reflection to stay Java 8 compatible > - String currentPid = getCurrentPid(); > - if (currentPid == null) { > - // Skip test if we can't get PID > - return; > - } > - > - ProcessChecker checker = ProcessChecker.of(currentPid); > - > - assertThat(checker).isNotNull(); > - assertThat(checker.canUse()).isTrue(); > - assertThat(checker.isProcessAlive()).isTrue(); > - assertThat(checker.isStopped()).isFalse(); > - } > - > - @Test > - public void shouldSelectProcessHandleCheckerOnJava9Plus() { > - if (!ProcessChecker.isProcessHandleSupported()) { > - // Skip test if ProcessHandle is not available (Java 8) > - return; > - } > - > - String currentPid = getCurrentPid(); > - if (currentPid == null) { > - return; > - } > - > - ProcessChecker checker = ProcessChecker.of(currentPid); > - > assertThat(checker.getClass().getSimpleName()).isEqualTo("ProcessHandleChecker"); > - } > - > - @Test > - public void shouldStopChecker() { > - String currentPid = getCurrentPid(); > - if (currentPid == null) { > - return; > - } > - > - ProcessChecker checker = ProcessChecker.of(currentPid); > - > - assertThat(checker.canUse()).isTrue(); > - assertThat(checker.isStopped()).isFalse(); > - > - checker.stop(); > - > - assertThat(checker.isStopped()).isTrue(); > - assertThat(checker.canUse()).isFalse(); > - } > - > - @Test > - public void shouldDestroyActiveCommands() { > - String currentPid = getCurrentPid(); > - if (currentPid == null) { > - return; > - } > - > - ProcessChecker checker = ProcessChecker.of(currentPid); > - assertThat(checker.canUse()).isTrue(); > - > - checker.destroyActiveCommands(); > - > - assertThat(checker.isStopped()).isTrue(); > - assertThat(checker.canUse()).isFalse(); > - } > - > - @Test > - public void shouldHandleNonExistentProcess() { > - // Use an invalid PID that's unlikely to exist > - ProcessChecker checker = > ProcessChecker.of(Long.toString(Long.MAX_VALUE)); > - > - assertThat(checker).isNotNull(); > - > - assertThat(checker.canUse()).isTrue(); > - > - assertThat(checker.isProcessAlive()).isFalse(); > - } > - > - /** > - * Gets the current process PID in a way that works on both Java 8 and > Java 9+. > - */ > - private static String getCurrentPid() { > - // Try ProcessHandle (Java 9+) first via reflection > - try { > - Class<?> processHandleClass = > Class.forName("java.lang.ProcessHandle"); > - Object currentHandle = > processHandleClass.getMethod("current").invoke(null); > - Long pid = (Long) > processHandleClass.getMethod("pid").invoke(currentHandle); > - return String.valueOf(pid); > - } catch (Exception e) { > - // Fall back to ManagementFactory (works on Java 8) > - try { > - String name = > java.lang.management.ManagementFactory.getRuntimeMXBean() > - .getName(); > - // Format is "pid@hostname" > - int atIndex = name.indexOf('@'); > - if (atIndex > 0) { > - return name.substring(0, atIndex); > - } > - } catch (Exception ex) { > - // Ignore > - } > - } > - return null; > - } > -} > diff --git > a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ProcessHandleCheckerTest.java > > b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ProcessHandleCheckerTest.java > deleted file mode 100644 > index cc60e6122..000000000 > --- > a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ProcessHandleCheckerTest.java > +++ /dev/null > @@ -1,205 +0,0 @@ > -/* > - * Licensed to the Apache Software Foundation (ASF) under one > - * or more contributor license agreements. See the NOTICE file > - * distributed with this work for additional information > - * regarding copyright ownership. The ASF licenses this file > - * to you under the Apache License, Version 2.0 (the > - * "License"); you may not use this file except in compliance > - * with the License. You may obtain a copy of the License at > - * > - * http://www.apache.org/licenses/LICENSE-2.0 > - * > - * Unless required by applicable law or agreed to in writing, > - * software distributed under the License is distributed on an > - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > - * KIND, either express or implied. See the License for the > - * specific language governing permissions and limitations > - * under the License. > - */ > -package org.apache.maven.surefire.booter; > - > -import java.lang.management.ManagementFactory; > - > -import org.junit.Assume; > -import org.junit.Test; > - > -import static org.assertj.core.api.Assertions.assertThat; > -import static org.assertj.core.api.Assertions.assertThatThrownBy; > -import static org.junit.Assume.assumeTrue; > - > -/** > - * Tests for {@link ProcessHandleChecker}. > - * <p> > - * These tests use reflection-based PID detection to work on both Java 8 and > Java 9+. > - */ > -public class ProcessHandleCheckerTest { > - > - @Test > - public void shouldReportAvailableOnJava9Plus() { > - // This test runs on modern JVMs, so isAvailable() should return true > - // FIXME DisabledOnJre when we migrate to junit5 > - double v = > Double.parseDouble(System.getProperty("java.specification.version")); > - Assume.assumeTrue(v >= 9.0); > - assertThat(ProcessHandleChecker.isAvailable()).isTrue(); > - } > - > - @Test > - public void shouldDetectCurrentProcessAsAlive() { > - assumeTrue("ProcessHandle not available", > ProcessHandleChecker.isAvailable()); > - > - String currentPid = getCurrentPid(); > - assumeTrue("Could not determine current PID", currentPid != null); > - > - ProcessHandleChecker checker = new ProcessHandleChecker(currentPid); > - > - assertThat(checker.canUse()).isTrue(); > - assertThat(checker.isProcessAlive()).isTrue(); > - assertThat(checker.isStopped()).isFalse(); > - } > - > - @Test > - public void shouldDetectNonExistentProcessAsNotUsable() { > - assumeTrue("ProcessHandle not available", > ProcessHandleChecker.isAvailable()); > - > - // Use an invalid PID that's unlikely to exist > - ProcessHandleChecker checker = new ProcessHandleChecker("999999999"); > - > - assertThat(checker.canUse()).isTrue(); > - } > - > - @Test > - public void shouldStopChecker() { > - assumeTrue("ProcessHandle not available", > ProcessHandleChecker.isAvailable()); > - > - String currentPid = getCurrentPid(); > - assumeTrue("Could not determine current PID", currentPid != null); > - > - ProcessHandleChecker checker = new ProcessHandleChecker(currentPid); > - > - assertThat(checker.canUse()).isTrue(); > - assertThat(checker.isStopped()).isFalse(); > - > - checker.stop(); > - > - assertThat(checker.isStopped()).isTrue(); > - assertThat(checker.canUse()).isFalse(); > - } > - > - @Test > - public void shouldDestroyActiveCommands() { > - assumeTrue("ProcessHandle not available", > ProcessHandleChecker.isAvailable()); > - > - String currentPid = getCurrentPid(); > - assumeTrue("Could not determine current PID", currentPid != null); > - > - ProcessHandleChecker checker = new ProcessHandleChecker(currentPid); > - > - assertThat(checker.canUse()).isTrue(); > - > - checker.destroyActiveCommands(); > - > - assertThat(checker.isStopped()).isTrue(); > - assertThat(checker.canUse()).isFalse(); > - } > - > - @Test > - public void shouldReturnMeaningfulToString() { > - assumeTrue("ProcessHandle not available", > ProcessHandleChecker.isAvailable()); > - > - String currentPid = getCurrentPid(); > - assumeTrue("Could not determine current PID", currentPid != null); > - > - ProcessHandleChecker checker = new ProcessHandleChecker(currentPid); > - > - String toString = checker.toString(); > - > - assertThat(toString) > - .contains("ProcessHandleChecker") > - .contains("pid=" + currentPid) > - .contains("stopped=false"); > - } > - > - @Test > - public void shouldReturnToStringWithStartInstantAfterCanUse() { > - assumeTrue("ProcessHandle not available", > ProcessHandleChecker.isAvailable()); > - > - String currentPid = getCurrentPid(); > - assumeTrue("Could not determine current PID", currentPid != null); > - > - ProcessHandleChecker checker = new ProcessHandleChecker(currentPid); > - > - checker.canUse(); > - String toString = checker.toString(); > - > - > assertThat(toString).contains("ProcessHandleChecker").contains("hasHandle=true"); > - } > - > - @Test > - public void shouldCreateViaFactoryMethod() { > - assumeTrue("ProcessHandle not available", > ProcessHandleChecker.isAvailable()); > - > - String currentPid = getCurrentPid(); > - assumeTrue("Could not determine current PID", currentPid != null); > - > - ProcessChecker checker = ProcessChecker.of(currentPid); > - > - assertThat(checker).isInstanceOf(ProcessHandleChecker.class); > - assertThat(checker.canUse()).isTrue(); > - assertThat(checker.isProcessAlive()).isTrue(); > - } > - > - @Test > - public void shouldReturnNullFromFactoryForNullPpid() { > - ProcessChecker checker = ProcessChecker.of(null); > - > - assertThat(checker).isNull(); > - } > - > - @Test > - public void shouldThrowOnInvalidPpidFormat() { > - assertThatThrownBy(() -> new > ProcessHandleChecker("not-a-number")).isInstanceOf(NumberFormatException.class); > - } > - > - @Test > - public void shouldReturnProcessInfoAfterCanUse() { > - assumeTrue("ProcessHandle not available", > ProcessHandleChecker.isAvailable()); > - > - String currentPid = getCurrentPid(); > - assumeTrue("Could not determine current PID", currentPid != null); > - > - ProcessHandleChecker checker = new ProcessHandleChecker(currentPid); > - > - // Now processInfo() should return valid info > - ProcessInfo processInfo = checker.processInfo(); > - assertThat(processInfo).isNotNull(); > - assertThat(processInfo.getPID()).isEqualTo(currentPid); > - assertThat(processInfo.getTime()).isGreaterThan(0L); > - } > - > - /** > - * Gets the current process PID using reflection (Java 8 compatible). > - * > - * @return the current process PID as a string, or null if it cannot be > determined > - */ > - private static String getCurrentPid() { > - // Try ProcessHandle.current().pid() via reflection (Java 9+) > - try { > - Class<?> processHandleClass = > Class.forName("java.lang.ProcessHandle"); > - Object currentHandle = > processHandleClass.getMethod("current").invoke(null); > - Long pid = (Long) > processHandleClass.getMethod("pid").invoke(currentHandle); > - return String.valueOf(pid); > - } catch (Exception e) { > - // Fall back to ManagementFactory (works on Java 8) > - try { > - String name = ManagementFactory.getRuntimeMXBean().getName(); > - int atIndex = name.indexOf('@'); > - if (atIndex > 0) { > - return name.substring(0, atIndex); > - } > - } catch (Exception ex) { > - // Ignore > - } > - } > - return null; > - } > -} >
--------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
