diff --git a/tests/integration/hardhatprocess.nim b/tests/integration/hardhatprocess.nim index 82d34fce..b00b0400 100644 --- a/tests/integration/hardhatprocess.nim +++ b/tests/integration/hardhatprocess.nim @@ -14,6 +14,7 @@ import pkg/codex/conf import pkg/codex/utils/trackedfutures import ./codexclient import ./nodeprocess +import ./utils export codexclient export chronicles @@ -159,20 +160,6 @@ method onOutputLineCaptured(node: HardhatProcess, line: string) = discard logFile.closeFile() node.logFile = none IoHandle -proc killHardhatByPort( - port: int -): Future[CommandExResponse] {. - async: ( - raises: [ - AsyncProcessError, AsyncProcessTimeoutError, CancelledError, ValueError, OSError - ] - ) -.} = - let path = splitFile(currentSourcePath()).dir / "scripts" / "winkillhardhat.sh" - let cmd = &"{absolutePath(path)} killvendorport {port}" - trace "Forcefully killing windows hardhat process", port, cmd - return await execCommandEx(cmd, timeout = 5.seconds) - proc closeProcessStreams(node: HardhatProcess) {.async: (raises: []).} = when not defined(windows): if not node.process.isNil: @@ -186,7 +173,7 @@ proc closeProcessStreams(node: HardhatProcess) {.async: (raises: []).} = error "Failed to get port from Hardhat args" return try: - let cmdResult = await killHardhatByPort(port) + let cmdResult = await forceKillProcess("node.exe", &"--port {port}") if cmdResult.status > 0: error "Failed to forcefully kill windows hardhat process", port, exitCode = cmdResult.status, stderr = cmdResult.stdError diff --git a/tests/integration/scripts/winkillhardhat.sh b/tests/integration/scripts/winkillhardhat.sh deleted file mode 100644 index 32730d7d..00000000 --- a/tests/integration/scripts/winkillhardhat.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash - -# List all node.exe processes with command line arguments -list_node_processes() { - powershell.exe -Command "Get-WmiObject Win32_Process -Filter \"name = 'node.exe'\" | Select-Object ProcessId, Name, CommandLine | Format-Table -AutoSize" -} - -# Find node processes containing vendor\codex-contracts-eth in command line -find_vendor_node_processes() { - echo "Looking for node processes with 'vendor\\codex-contracts-eth' in command line..." - powershell.exe -Command "Get-WmiObject Win32_Process -Filter \"name = 'node.exe'\" | Where-Object { \$_.CommandLine -match 'vendor\\\\codex-contracts-eth' } | Select-Object ProcessId, Name, CommandLine | Format-Table -AutoSize" -} - -# Find node processes running on a specific port -find_node_by_port() { - local port=$1 - echo "Looking for node processes running on port $port..." - powershell.exe -Command "Get-WmiObject Win32_Process -Filter \"name = 'node.exe'\" | Where-Object { \$_.CommandLine -match '--port $port' } | Select-Object ProcessId, Name, CommandLine | Format-Table -AutoSize" -} - -# Kill all node.exe processes containing vendor\codex-contracts-eth -kill_vendor_node_processes() { - echo "Finding and killing node.exe processes containing 'vendor\\codex-contracts-eth'..." - powershell.exe -Command " - \$processes = Get-WmiObject Win32_Process -Filter \"name = 'node.exe'\" | Where-Object { \$_.CommandLine -match 'vendor\\\\codex-contracts-eth' }; - if (\$processes) { - foreach (\$process in \$processes) { - Stop-Process -Id \$process.ProcessId -Force; - Write-Host \"Killed process \$(\$process.ProcessId)\"; - } - } else { - Write-Host \"No matching node.exe processes found\"; - } - " -} - -# Kill node.exe process running on a specific port -kill_node_by_port() { - local port=$1 - echo "Finding and killing node.exe process running on port $port..." - powershell.exe -Command " - \$processes = Get-WmiObject Win32_Process -Filter \"name = 'node.exe'\" | Where-Object { \$_.CommandLine -match '--port $port' }; - if (\$processes) { - foreach (\$process in \$processes) { - Stop-Process -Id \$process.ProcessId -Force; - Write-Host \"Killed process \$(\$process.ProcessId) running on port $port\"; - } - } else { - Write-Host \"No node.exe process found running on port $port\"; - } - " -} - -# Kill node.exe process with both vendor string and specific port -kill_vendor_node_by_port() { - local port=$1 - echo "Finding and killing node.exe process with 'vendor\\codex-contracts-eth' running on port $port..." - powershell.exe -Command " - \$processes = Get-WmiObject Win32_Process -Filter \"name = 'node.exe'\" | Where-Object { \$_.CommandLine -match 'vendor\\\\codex-contracts-eth' -and \$_.CommandLine -match '--port $port' }; - if (\$processes) { - foreach (\$process in \$processes) { - Stop-Process -Id \$process.ProcessId -Force; - Write-Host \"Killed process \$(\$process.ProcessId) running on port $port\"; - } - } else { - Write-Host \"No matching node.exe process found running on port $port\"; - } - " -} - -# Check if being run directly or sourced -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - # If run directly (not sourced), provide command line interface - case "$1" in - list) - list_node_processes - ;; - find) - find_vendor_node_processes - ;; - findport) - if [ -z "$2" ]; then - echo "Usage: $0 findport PORT_NUMBER" - exit 1 - fi - find_node_by_port "$2" - ;; - killall) - kill_vendor_node_processes - ;; - killport) - if [ -z "$2" ]; then - echo "Usage: $0 killport PORT_NUMBER" - exit 1 - fi - kill_node_by_port "$2" - ;; - killvendorport) - if [ -z "$2" ]; then - echo "Usage: $0 killvendorport PORT_NUMBER" - exit 1 - fi - kill_vendor_node_by_port "$2" - ;; - *) - echo "Usage: $0 {list|find|findport PORT|killall|killport PORT|killvendorport PORT}" - exit 1 - ;; - esac -fi \ No newline at end of file diff --git a/tests/integration/scripts/winkillprocess.sh b/tests/integration/scripts/winkillprocess.sh new file mode 100644 index 00000000..b5e58ab4 --- /dev/null +++ b/tests/integration/scripts/winkillprocess.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# List all processes with a specific name +list() { + local name=$1 + echo "Listing all processes named '$name'..." + powershell.exe -Command "Get-CimInstance Win32_Process -Filter \"name = '$name'\" | Select-Object ProcessId, Name, CommandLine | Format-Table -AutoSize" +} + +# Search for processes with a specific name and command line pattern +search() { + local name=$1 + local pattern=$2 + echo "Searching for '$name' processes with command line matching '$pattern'..." + powershell.exe -Command " + \$processes = Get-CimInstance Win32_Process -Filter \"name = '$name'\" | Where-Object { \$_.CommandLine -match '$pattern' }; + if (\$processes) { + \$processes | Select-Object ProcessId, Name, CommandLine | Format-Table -AutoSize; + } else { + Write-Host \"No matching '$name' processes found\"; + } + " +} + +# Kill all processes with a specific name +killall() { + local name=$1 + echo "Finding and killing all '$name' processes..." + powershell.exe -Command " + \$processes = Get-CimInstance Win32_Process -Filter \"name = '$name'\"; + if (\$processes) { + foreach (\$process in \$processes) { + Stop-Process -Id \$process.ProcessId -Force; + Write-Host \"Killed process \$(\$process.ProcessId)\"; + } + } else { + Write-Host \"No '$name' processes found\"; + } + " +} + +# Kill processes with a specific name and command line pattern +kill() { + local name=$1 + local pattern=$2 + echo "Finding and killing '$name' processes with command line matching '$pattern'..." + powershell.exe -Command " + \$processes = Get-CimInstance Win32_Process -Filter \"name = '$name'\" | Where-Object { \$_.CommandLine -match '$pattern' }; + if (\$processes) { + foreach (\$process in \$processes) { + Stop-Process -Id \$process.ProcessId -Force; + Write-Host \"Killed process \$(\$process.ProcessId)\"; + } + } else { + Write-Host \"No matching '$name' processes found\"; + } + " +} + +# Check if being run directly or sourced +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # If run directly (not sourced), provide command line interface + case "$1" in + list) + if [ -z "$2" ]; then + echo "Usage: $0 list PROCESS_NAME" + exit 1 + fi + list "$2" + ;; + search) + if [ -z "$2" ] || [ -z "$3" ]; then + echo "Usage: $0 search PROCESS_NAME COMMANDLINE_PATTERN" + exit 1 + fi + search "$2" "$3" + ;; + killall) + if [ -z "$2" ]; then + echo "Usage: $0 killall PROCESS_NAME" + exit 1 + fi + killall "$2" + ;; + kill) + if [ -z "$2" ] || [ -z "$3" ]; then + echo "Usage: $0 kill PROCESS_NAME COMMANDLINE_PATTERN" + exit 1 + fi + kill "$2" "$3" + ;; + *) + echo "Usage: $0 {list PROCESS_NAME|search PROCESS_NAME COMMANDLINE_PATTERN|killall PROCESS_NAME|kill PROCESS_NAME COMMANDLINE_PATTERN}" + exit 1 + ;; + esac +fi diff --git a/tests/integration/testmanager.nim b/tests/integration/testmanager.nim index 6b10e94a..a41935a2 100644 --- a/tests/integration/testmanager.nim +++ b/tests/integration/testmanager.nim @@ -3,7 +3,6 @@ import std/strformat import std/terminal from std/times import fromUnix, format, now from std/unicode import toUpper -import std/unittest import pkg/chronos import pkg/chronos/asyncproc import pkg/codex/conf @@ -389,6 +388,38 @@ proc teardownHardhat(test: IntegrationTest, hardhat: Hardhat) {.async: (raises: test.manager.hardhats.keepItIf(it != hardhat) +proc closeProcessStreams(test: IntegrationTest) {.async: (raises: []).} = + logScope: + name = test.config.name + + when not defined(windows): + if not test.process.isNil: + trace "Closing test process' streams" + await test.process.closeWait() + trace "Test process' streams closed" + else: + # Windows hangs when attempting to close the test's process streams, so try + # to kill the process externally. + try: + let cmdResult = await test.forceKillProcess("nim.exe", &"-d:TestId {test.testId}") + if cmdResult.status > 0: + error "Failed to forcefully kill windows test process", + port, exitCode = cmdResult.status, stderr = cmdResult.stdError + else: + trace "Successfully killed windows test process by force", + port, exitCode = cmdResult.status, stdout = cmdResult.stdOutput + except ValueError, OSError: + let eMsg = getCurrentExceptionMsg() + error "Failed to forcefully kill windows test process, bad path to command", + error = eMsg + except CancelledError as e: + discard + except AsyncProcessError as e: + error "Failed to forcefully kill windows test process", port, error = e.msg + except AsyncProcessTimeoutError as e: + error "Timeout while forcefully killing windows test process", + port, error = e.msg + proc teardownTest(test: IntegrationTest) {.async: (raises: []).} = logScope: test = test.config.name @@ -409,8 +440,8 @@ proc teardownTest(test: IntegrationTest) {.async: (raises: []).} = let e = getCurrentException() warn "Test process failed to terminate, check for zombies", error = e.msg - await test.process.closeWait() - trace "Test process output streams closed" + await test.closeProcessStreams() + test.process = nil proc teardown(test: IntegrationTest, hardhat: ?Hardhat) {.async: (raises: []).} = if test.config.startHardhat and hardhat =? hardhat and not hardhat.process.isNil: diff --git a/tests/integration/utils.nim b/tests/integration/utils.nim index 4ce8fcda..974ec7d6 100644 --- a/tests/integration/utils.nim +++ b/tests/integration/utils.nim @@ -1,5 +1,6 @@ import std/os import pkg/chronos +import pkg/chronos/asyncproc import pkg/codex/logutils {.push raises: [].} @@ -64,3 +65,18 @@ proc appendFile*(filename: string, content: string) {.raises: [IOError].} = raise newException(IOError, "cannot open and write " & filename & ": " & e.msg) finally: close(f) + +when defined(windows): + proc forceKillProcess( + processName, matchingCriteria: string + ): Future[CommandExResponse] {. + async: ( + raises: [ + AsyncProcessError, AsyncProcessTimeoutError, CancelledError, ValueError, OSError + ] + ) + .} = + let path = splitFile(currentSourcePath()).dir / "scripts" / "winkillprocess.sh" + let cmd = &"{absolutePath(path)} kill {processName} \"{matchingCriteria}\"" + trace "Forcefully killing windows process", processName, matchingCriteria, cmd + return await execCommandEx(cmd, timeout = 5.seconds)