2025-06-04 16:01:27 +10:00

85 lines
2.7 KiB
Nim

import std/os
import std/strformat
import pkg/chronos
import pkg/chronos/asyncproc
import pkg/codex/logutils
{.push raises: [].}
proc nextFreePort*(startPort: int): Future[int] {.async: (raises: [CancelledError]).} =
proc client(server: StreamServer, transp: StreamTransport) {.async: (raises: []).} =
await transp.closeWait()
var port = startPort
while true:
trace "checking if port is free", port
try:
let host = initTAddress("127.0.0.1", port)
# We use ReuseAddr here only to be able to reuse the same IP/Port when
# there's a TIME_WAIT socket. It's useful when running the test multiple
# times or if a test ran previously using the same port.
var server = createStreamServer(host, client, {ReuseAddr})
trace "port is free", port
await server.closeWait()
return port
except TransportOsError:
trace "port is not free", port
inc port
except TransportAddressError:
raiseAssert "bad address"
proc sanitize*(pathSegment: string): string =
var sanitized = pathSegment
for invalid in invalidFilenameChars.items:
sanitized = sanitized.replace(invalid, '_').replace(' ', '_')
sanitized
proc getLogFile*(
logDir, startTime, suiteName, testName, role: string, index = int.none
): string {.raises: [IOError, OSError].} =
let logsDir =
if logDir == "":
currentSourcePath.parentDir() / "logs" / sanitize(startTime & "__" & suiteName) /
sanitize(testName)
else:
logDir / sanitize(suiteName) / sanitize(testName)
createDir(logsDir)
var fn = $role
if idx =? index:
fn &= "_" & $idx
fn &= ".log"
let fileName = logsDir / fn
return fileName
proc appendFile*(filename: string, content: string) {.raises: [IOError].} =
## Opens a file named `filename` for writing. Then writes the
## `content` completely to the file and closes the file afterwards.
## Raises an IO exception in case of an error.
var f: File
try:
f = open(filename, fmAppend)
f.write(content)
except IOError as e:
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)