-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]

Reply via email to