This is an automated email from the ASF dual-hosted git repository. tibordigana pushed a commit to branch windows-wmic in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
commit 549ad0d4ce213c98d8507e678676bdc1fa45b0dd Author: tibordigana <[email protected]> AuthorDate: Sat Feb 21 21:25:30 2026 +0100 Supporting WMIC and PowerShell --- .../apache/maven/surefire/booter/PpidChecker.java | 92 +++++++++++++++------- .../apache/maven/surefire/booter/ProcessInfo.java | 3 +- .../maven/surefire/booter/PpidCheckerTest.java | 2 +- 3 files changed, 68 insertions(+), 29 deletions(-) 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 2c836ea21..0100cd78e 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 @@ -23,6 +23,7 @@ import java.io.File; import java.io.IOException; import java.io.InterruptedIOException; +import java.math.BigDecimal; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.Queue; @@ -62,17 +63,23 @@ * @since 2.20.1 */ final class PpidChecker { + private static final BigDecimal THOUSAND = new BigDecimal(1000); 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; private static final int WMIC_CREATION_DATE_TIMESTAMP_LENGTH = 18; private static final SimpleDateFormat WMIC_CREATION_DATE_FORMAT = IS_OS_WINDOWS ? createWindowsCreationDateFormat() : null; + private static final Pattern POWERSHELL_CPU_PATTERN = Pattern.compile("^\\d+(\\.\\d+)?$"); private static final String WMIC_CREATION_DATE = "CreationDate"; + private static final String POWERSHELL_CMD_HEADER = "CPU"; private static final String WINDOWS_SYSTEM_ROOT_ENV = "SystemRoot"; - private static final String RELATIVE_PATH_TO_WMIC = "System32\\Wbem"; - private static final String SYSTEM_PATH_TO_WMIC = - "%" + WINDOWS_SYSTEM_ROOT_ENV + "%\\" + RELATIVE_PATH_TO_WMIC + "\\"; + private static final String RELATIVE_PATH_TO_WMIC32 = "System32\\Wbem"; + private static final String RELATIVE_PATH_TO_WMIC64 = "SysWOW64\\Wbem"; + private static final String SYSTEM_PATH_TO_WMIC32 = + "%" + WINDOWS_SYSTEM_ROOT_ENV + "%\\" + RELATIVE_PATH_TO_WMIC32 + "\\"; + private static final String SYSTEM_PATH_TO_WMIC64 = + "%" + WINDOWS_SYSTEM_ROOT_ENV + "%\\" + RELATIVE_PATH_TO_WMIC64 + "\\"; private static final String PS_ETIME_HEADER = "ELAPSED"; private static final String PS_PID_HEADER = "PID"; @@ -159,7 +166,7 @@ ProcessInfo unix() { ProcessInfoConsumer reader = new ProcessInfoConsumer(charset) { @Override @Nonnull - ProcessInfo consumeLine(String line, ProcessInfo previousOutputLine) { + protected ProcessInfo consumeLine(String line, ProcessInfo previousOutputLine) { if (previousOutputLine.isInvalid()) { if (hasHeader) { Matcher matcher = UNIX_CMD_OUT_PATTERN.matcher(line); @@ -187,36 +194,61 @@ ProcessInfo consumeLine(String line, ProcessInfo previousOutputLine) { } ProcessInfo windows() { - ProcessInfoConsumer reader = new ProcessInfoConsumer("US-ASCII") { + class WindowsProcessInfoConsumer extends ProcessInfoConsumer { + private boolean isPowershell; + + WindowsProcessInfoConsumer() { + super("US-ASCII"); + } + @Override @Nonnull - ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception { + protected ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception { if (previousProcessInfo.isInvalid() && !line.isEmpty()) { if (hasHeader) { - // now the line is CreationDate, e.g. 20180406142327.741074+120 - if (line.length() != WMIC_CREATION_DATE_VALUE_LENGTH) { - throw new IllegalStateException("WMIC CreationDate should have 25 characters " + line); + if (isPowershell) { + if (POWERSHELL_CPU_PATTERN.matcher(line).matches()) { + long etime = new BigDecimal(line).multiply(THOUSAND).longValue(); + return windowsProcessInfo(ppid, etime); + } + } else { + // now the line is CreationDate, e.g. 20180406142327.741074+120 + if (line.length() != WMIC_CREATION_DATE_VALUE_LENGTH) { + throw new IllegalStateException("WMIC CreationDate should have 25 characters " + line); + } + String startTimestamp = line.substring(0, WMIC_CREATION_DATE_TIMESTAMP_LENGTH); + int indexOfTimeZone = WMIC_CREATION_DATE_VALUE_LENGTH - 4; + long startTimestampMillisUTC = WMIC_CREATION_DATE_FORMAT.parse(startTimestamp).getTime() + - parseInt(line.substring(indexOfTimeZone)) * MINUTES_TO_MILLIS; + return windowsProcessInfo(ppid, startTimestampMillisUTC); } - String startTimestamp = line.substring(0, WMIC_CREATION_DATE_TIMESTAMP_LENGTH); - int indexOfTimeZone = WMIC_CREATION_DATE_VALUE_LENGTH - 4; - long startTimestampMillisUTC = - WMIC_CREATION_DATE_FORMAT.parse(startTimestamp).getTime() - - parseInt(line.substring(indexOfTimeZone)) * MINUTES_TO_MILLIS; - return windowsProcessInfo(ppid, startTimestampMillisUTC); } else { - hasHeader = WMIC_CREATION_DATE.equals(line); + hasHeader = WMIC_CREATION_DATE.equals(line) || POWERSHELL_CMD_HEADER.equals(line); } } return previousProcessInfo; } - }; - String wmicPath = hasWmicStandardSystemPath() ? SYSTEM_PATH_TO_WMIC : ""; - return reader.execute( - "CMD", - "/A", - "/X", - "/C", - wmicPath + "wmic process where (ProcessId=" + ppid + ") get " + WMIC_CREATION_DATE); + + ProcessInfo checkPpid() { + if (hasWmicStandardSystemPath64()) { + return execute("CMD", "/A", "/X", "/C", + SYSTEM_PATH_TO_WMIC64 + + "wmic process where (ProcessId=" + ppid + ") get " + WMIC_CREATION_DATE); + } else if (hasWmicStandardSystemPath32()) { + return execute("CMD", "/A", "/X", "/C", + SYSTEM_PATH_TO_WMIC32 + + "wmic process where (ProcessId=" + ppid + ") get " + WMIC_CREATION_DATE); + } else { + isPowershell = true; + return execute("CMD", "/A", "/X", "/C", + "powershell", " \"Get-Process -Id " + ppid + " | Select-Object -Property CPU" + "\""); + } + } + } + + WindowsProcessInfoConsumer reader = new WindowsProcessInfoConsumer(); + return reader.checkPpid(); + } void destroyActiveCommands() { @@ -254,9 +286,14 @@ private static boolean canExecuteStandardUnixPs() { } } - private static boolean hasWmicStandardSystemPath() { + private static boolean hasWmicStandardSystemPath32() { + String systemRoot = System.getenv(WINDOWS_SYSTEM_ROOT_ENV); + return isNotBlank(systemRoot) && new File(systemRoot, RELATIVE_PATH_TO_WMIC32 + "\\wmic.exe").isFile(); + } + + private static boolean hasWmicStandardSystemPath64() { String systemRoot = System.getenv(WINDOWS_SYSTEM_ROOT_ENV); - return isNotBlank(systemRoot) && new File(systemRoot, RELATIVE_PATH_TO_WMIC + "\\wmic.exe").isFile(); + return isNotBlank(systemRoot) && new File(systemRoot, RELATIVE_PATH_TO_WMIC64 + "\\wmic.exe").isFile(); } static long fromDays(Matcher matcher) { @@ -337,7 +374,8 @@ abstract class ProcessInfoConsumer { this.charset = charset; } - abstract @Nonnull ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) throws Exception; + protected abstract @Nonnull ProcessInfo consumeLine(String line, ProcessInfo previousProcessInfo) + throws Exception; ProcessInfo execute(String... command) { ProcessBuilder processBuilder = new ProcessBuilder(command); 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 771445965..11f77f852 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 @@ -34,6 +34,7 @@ final class ProcessInfo { static final ProcessInfo INVALID_PROCESS_INFO = new ProcessInfo(null, 0); static final ProcessInfo ERR_PROCESS_INFO = new ProcessInfo(null, 0); + static final ProcessInfo ERR_CMD_NOT_FOUND = new ProcessInfo(null, 0); /** * On Unix we do not get PID due to the command is interested only to etime of PPID: @@ -65,7 +66,7 @@ boolean isInvalid() { } boolean isError() { - return this == ERR_PROCESS_INFO; + return this == ERR_PROCESS_INFO || this == ERR_CMD_NOT_FOUND; } String getPID() { 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 index 78f35716f..83e5f8dbe 100644 --- 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 @@ -371,7 +371,7 @@ public void shouldHaveSystemPathToWmicOnWindows() throws Exception { assumeThat(System.getenv("SystemRoot"), is(not(""))); assumeTrue(new File(System.getenv("SystemRoot"), "System32\\Wbem").isDirectory()); assumeTrue(new File(System.getenv("SystemRoot"), "System32\\Wbem\\wmic.exe").isFile()); - assertThat((Boolean) invokeMethod(PpidChecker.class, "hasWmicStandardSystemPath")) + assertThat((Boolean) invokeMethod(PpidChecker.class, "hasWmicStandardSystemPath32")) .isTrue(); assertThat(new File(System.getenv("SystemRoot"), "System32\\Wbem\\wmic.exe")) .isFile();
