2024-12-20 16:23:40 +11:00
import std / os
import std / strformat
2025-01-10 23:22:34 +11:00
import std / terminal
2025-01-13 14:38:26 +11:00
from std / unicode import toUpper
2025-01-10 23:22:34 +11:00
import std / unittest
2024-12-20 16:23:40 +11:00
import pkg / chronos
import pkg / chronos / asyncproc
import pkg / codex / logutils
import pkg / questionable
import pkg / questionable / results
import . / hardhatprocess
import . / utils
import .. / examples
type
2025-01-14 17:48:24 +11:00
Hardhat = ref object
process : HardhatProcess
output : seq [ string ]
port : int
2024-12-20 16:23:40 +11:00
TestManager * = ref object
configs : seq [ IntegrationTestConfig ]
tests : seq [ IntegrationTest ]
2025-01-14 17:48:24 +11:00
hardhats : seq [ Hardhat ]
2024-12-20 16:23:40 +11:00
lastHardhatPort : int
lastCodexApiPort : int
lastCodexDiscPort : int
2025-01-16 11:52:02 +11:00
# Echoes stderr if there's a test failure (eg test failed, compilation
# error) or error (eg test manager error)
debugTestHarness : bool
# Echoes stdout from Hardhat process
2024-12-20 16:23:40 +11:00
debugHardhat : bool
2025-01-16 11:52:02 +11:00
# Echoes stdout from the integration test file process. Codex process logs
# can also be output if a test uses a multinodesuite, requires
# CodexConfig.debug to be enabled
debugCodexNodes : bool
timeStart : ? Moment
timeEnd : ? Moment
2024-12-20 16:23:40 +11:00
codexPortLock : AsyncLock
hardhatPortLock : AsyncLock
2025-01-15 10:40:12 +11:00
hardhatProcessLock : AsyncLock
2025-01-10 23:22:34 +11:00
testTimeout : Duration # individual test timeout
2025-01-16 11:52:51 +11:00
futContinuousUpdates : Future [ void ] . Raising ( [ ] )
2024-12-20 16:23:40 +11:00
IntegrationTestConfig * = object
2025-01-13 14:39:07 +11:00
startHardhat : bool
testFile : string
name : string
2024-12-20 16:23:40 +11:00
2025-01-13 14:30:18 +11:00
IntegrationTestStatus = enum ## The status of a test when it is done.
2025-01-16 11:52:51 +11:00
New , # Test not yet run
Running , # Test currently running
2025-01-14 17:48:24 +11:00
Ok , # Test file launched, and exited with 0. Indicates all tests completed and passed.
Failed , # Test file launched, but exited with a non-zero exit code. Indicates either the test file did not compile, or one or more of the tests in the file failed
Timeout , # Test file launched, but the tests did not complete before the timeout.
Error # Test file did not launch correctly. Indicates an error occurred running the tests (usually an error in the harness).
2025-01-10 23:22:34 +11:00
2024-12-20 16:23:40 +11:00
IntegrationTest = ref object
2025-01-15 10:38:33 +11:00
manager : TestManager
2024-12-20 16:23:40 +11:00
config : IntegrationTestConfig
process : Future [ CommandExResponse ] . Raising ( [ AsyncProcessError , AsyncProcessTimeoutError , CancelledError ] )
2025-01-16 11:52:51 +11:00
timeStart : ? Moment
timeEnd : ? Moment
2024-12-20 16:23:40 +11:00
output : ? ! CommandExResponse
testId : string # when used in datadir path, prevents data dir clashes
2025-01-10 23:22:34 +11:00
status : IntegrationTestStatus
2024-12-20 16:23:40 +11:00
2025-01-14 14:56:30 +11:00
TestManagerError * = object of CatchableError
2025-01-13 14:30:18 +11:00
2025-01-14 17:52:33 +11:00
Border {. pure . } = enum
Left , Right
Align {. pure . } = enum
Left , Right
MarkerPosition {. pure . } = enum
Start ,
Finish
2024-12-20 16:23:40 +11:00
{. push raises : [ ] . }
logScope :
topics = " testing integration testmanager "
2025-01-15 10:39:41 +11:00
proc printOutputMarker ( test : IntegrationTest , position : MarkerPosition , msg : string ) {. gcsafe , raises : [ ] . }
2025-01-10 23:22:34 +11:00
proc raiseTestManagerError ( msg : string , parent : ref CatchableError = nil ) {. raises : [ TestManagerError ] . } =
raise newException ( TestManagerError , msg , parent )
2025-01-14 17:48:24 +11:00
template echoStyled ( args : varargs [ untyped ] ) =
try :
styledEcho args
except CatchableError as parent :
# no need to re-raise this, as it'll eventually have to be logged only
error " failed to print to terminal " , error = parent . msg
2025-01-10 23:22:34 +11:00
proc new * (
2024-12-20 16:23:40 +11:00
_ : type TestManager ,
configs : seq [ IntegrationTestConfig ] ,
debugTestHarness = false ,
2025-01-10 23:22:34 +11:00
debugHardhat = false ,
debugCodexNodes = false ,
testTimeout = 60 . minutes ) : TestManager =
2024-12-20 16:23:40 +11:00
TestManager (
configs : configs ,
lastHardhatPort : 8545 ,
lastCodexApiPort : 8000 ,
lastCodexDiscPort : 9000 ,
debugTestHarness : debugTestHarness ,
2025-01-10 23:22:34 +11:00
debugHardhat : debugHardhat ,
debugCodexNodes : debugCodexNodes ,
testTimeout : testTimeout
2024-12-20 16:23:40 +11:00
)
2025-01-13 14:39:07 +11:00
func init * (
_ : type IntegrationTestConfig ,
testFile : string ,
startHardhat : bool ,
name = " " ) : IntegrationTestConfig =
IntegrationTestConfig (
testFile : testFile ,
name : if name = = " " :
testFile . extractFilename
else :
name ,
startHardhat : startHardhat
)
2024-12-20 16:23:40 +11:00
template withLock * ( lock : AsyncLock , body : untyped ) =
if lock . isNil :
lock = newAsyncLock ( )
await lock . acquire ( )
try :
body
await sleepAsync ( 1 . millis )
finally :
try :
lock . release ( )
2025-01-10 23:22:34 +11:00
except AsyncLockError as parent :
raiseTestManagerError " lock error " , parent
proc duration ( manager : TestManager ) : Duration =
2025-01-16 11:52:51 +11:00
let now = Moment . now ( )
( manager . timeEnd | ? now ) - ( manager . timeStart | ? now )
2025-01-10 23:22:34 +11:00
proc duration ( test : IntegrationTest ) : Duration =
2025-01-16 11:52:51 +11:00
let now = Moment . now ( )
( test . timeEnd | ? now ) - ( test . timeStart | ? now )
2024-12-20 16:23:40 +11:00
proc startHardhat (
2025-01-15 10:38:33 +11:00
test : IntegrationTest ) : Future [ Hardhat ] {. async : ( raises : [ CancelledError , TestManagerError ] ) . } =
2024-12-20 16:23:40 +11:00
var args : seq [ string ] = @ [ ]
var port : int
2025-01-14 14:56:30 +11:00
let hardhat = Hardhat . new ( )
proc onOutputLineCaptured ( line : string ) {. raises : [ ] . } =
hardhat . output . add line
2025-01-15 10:38:33 +11:00
withLock ( test . manager . hardhatPortLock ) :
port = await nextFreePort ( test . manager . lastHardhatPort + 10 )
test . manager . lastHardhatPort = port
2024-12-20 16:23:40 +11:00
args . add ( " --port " )
args . add ( $ port )
trace " starting hardhat process on port " , port
try :
2025-01-15 10:40:12 +11:00
withLock ( test . manager . hardhatProcessLock ) :
let node = await HardhatProcess . startNode (
args ,
false ,
" hardhat for ' " & test . config . name & " ' " ,
onOutputLineCaptured )
hardhat . process = node
hardhat . port = port
await node . waitUntilStarted ( )
return hardhat
2024-12-20 16:23:40 +11:00
except CancelledError as e :
raise e
except CatchableError as e :
2025-01-15 10:39:41 +11:00
if not hardhat . isNil :
test . printOutputMarker ( MarkerPosition . Start , " hardhat stdout " )
for line in hardhat . output :
echo line
test . printOutputMarker ( MarkerPosition . Finish , " hardhat stdout " )
2024-12-20 16:23:40 +11:00
raiseTestManagerError " hardhat node failed to start: " & e . msg , e
2025-01-10 23:22:34 +11:00
proc printResult (
test : IntegrationTest ,
2025-01-14 14:56:30 +11:00
colour : ForegroundColor ) =
2025-01-10 23:22:34 +11:00
2025-01-14 17:48:24 +11:00
echoStyled styleBright , colour , & " [{toUpper $test .status}] " ,
2025-01-10 23:22:34 +11:00
resetStyle , test . config . name ,
resetStyle , styleDim , & " ({test.duration}) "
2025-01-13 14:38:26 +11:00
proc printOutputMarker (
test : IntegrationTest ,
position : MarkerPosition ,
2025-01-14 14:56:30 +11:00
msg : string ) =
2025-01-13 14:38:26 +11:00
2025-01-14 14:56:30 +11:00
if position = = MarkerPosition . Start :
echo " "
2025-01-13 14:38:26 +11:00
2025-01-14 17:48:24 +11:00
echoStyled styleBright , bgWhite , fgBlack ,
2025-01-14 14:56:30 +11:00
& " ----- {toUpper $position } {test.config.name} {msg} ----- "
if position = = MarkerPosition . Finish :
echo " "
2025-01-13 14:38:26 +11:00
2025-01-10 23:22:34 +11:00
proc printResult (
test : IntegrationTest ,
2025-01-14 17:48:24 +11:00
printStdOut = false ,
printStdErr = false ) =
2025-01-10 23:22:34 +11:00
2025-01-16 11:52:51 +11:00
case test . status :
of IntegrationTestStatus . New :
test . printResult ( fgBlue )
of IntegrationTestStatus . Running :
test . printResult ( fgCyan )
2025-01-10 23:22:34 +11:00
2025-01-16 11:52:51 +11:00
of IntegrationTestStatus . Error :
if error = ? test . output . errorOption :
test . printResult ( fgRed )
test . printOutputMarker ( MarkerPosition . Start , " test harness errors " )
echo " Error during test execution: " , error . msg
echo " Stacktrace: " , error . getStackTrace ( )
test . printOutputMarker ( MarkerPosition . Finish , " test harness errors " )
of IntegrationTestStatus . Failed :
2025-01-10 23:22:34 +11:00
if output = ? test . output :
2025-01-14 17:48:24 +11:00
if printStdErr : #manager.debugTestHarness
2025-01-13 14:38:26 +11:00
test . printOutputMarker ( MarkerPosition . Start ,
" test harness errors (stderr) " )
2025-01-10 23:22:34 +11:00
echo output . stdError
2025-01-13 14:38:26 +11:00
test . printOutputMarker ( MarkerPosition . Finish ,
" test harness errors (stderr) " )
2025-01-14 17:48:24 +11:00
if printStdOut :
2025-01-13 14:38:26 +11:00
test . printOutputMarker ( MarkerPosition . Start ,
" codex node output (stdout) " )
2025-01-10 23:22:34 +11:00
echo output . stdOutput
2025-01-13 14:38:26 +11:00
test . printOutputMarker ( MarkerPosition . Finish ,
" codex node output (stdout) " )
2025-01-10 23:22:34 +11:00
test . printResult ( fgRed )
2025-01-16 11:52:51 +11:00
of IntegrationTestStatus . Timeout :
2025-01-10 23:22:34 +11:00
test . printResult ( fgYellow )
2025-01-16 11:52:51 +11:00
of IntegrationTestStatus . Ok :
2025-01-14 17:48:24 +11:00
if printStdOut and
2025-01-13 14:30:18 +11:00
output = ? test . output :
2025-01-13 14:38:26 +11:00
test . printOutputMarker ( MarkerPosition . Start ,
" codex node output (stdout) " )
2025-01-10 23:22:34 +11:00
echo output . stdOutput
2025-01-13 14:38:26 +11:00
test . printOutputMarker ( MarkerPosition . Finish ,
" codex node output (stdout) " )
2025-01-10 23:22:34 +11:00
test . printResult ( fgGreen )
2025-01-14 14:56:30 +11:00
proc printSummary ( test : IntegrationTest ) =
2025-01-14 17:48:24 +11:00
test . printResult ( printStdOut = false , printStdErr = false )
2025-01-10 23:22:34 +11:00
2025-01-14 14:56:30 +11:00
proc printStart ( test : IntegrationTest ) =
2025-01-14 17:48:24 +11:00
echoStyled styleBright , fgMagenta , & " [Integration test started] " , resetStyle , test . config . name
2025-01-14 14:56:30 +11:00
2025-01-10 23:22:34 +11:00
proc buildCommand (
test : IntegrationTest ,
2025-01-14 14:56:30 +11:00
hardhatPort : ? int ) : Future [ string ] {. async : ( raises : [ CancelledError , TestManagerError ] ) . } =
2024-12-20 16:23:40 +11:00
var apiPort , discPort : int
2025-01-15 10:38:33 +11:00
withLock ( test . manager . codexPortLock ) :
2025-01-10 23:22:34 +11:00
# TODO: needed? nextFreePort should take care of this
2024-12-20 16:23:40 +11:00
# inc by 20 to allow each test to run 20 codex nodes (clients, SPs,
# validators) giving a good chance the port will be free
2025-01-15 10:38:33 +11:00
apiPort = await nextFreePort ( test . manager . lastCodexApiPort + 20 )
test . manager . lastCodexApiPort = apiPort
discPort = await nextFreePort ( test . manager . lastCodexDiscPort + 20 )
test . manager . lastCodexDiscPort = discPort
2024-12-20 16:23:40 +11:00
var logging = " "
2025-01-15 10:38:33 +11:00
if test . manager . debugTestHarness :
2024-12-20 16:23:40 +11:00
logging = " -d:chronicles_log_level=TRACE " &
" -d:chronicles_disabled_topics=websock " &
" -d:chronicles_default_output_device=stdout " &
" -d:chronicles_sinks=textlines "
2025-01-14 14:56:30 +11:00
let strHardhatPort =
if not test . config . startHardhat : " "
else :
without port = ? hardhatPort :
raiseTestManagerError " hardhatPort required when ' config.startHardhat ' is true "
" -d:HardhatPort= " & $ port
2024-12-20 16:23:40 +11:00
var testFile : string
try :
testFile = absolutePath (
2025-01-10 23:22:34 +11:00
test . config . testFile ,
2024-12-20 16:23:40 +11:00
root = currentSourcePath ( ) . parentDir ( ) . parentDir ( ) )
2025-01-10 23:22:34 +11:00
except ValueError as parent :
raiseTestManagerError " bad file name, testFile: " & test . config . testFile , parent
2024-12-20 16:23:40 +11:00
var command : string
2025-01-15 10:38:33 +11:00
withLock ( test . manager . hardhatPortLock ) :
2025-01-10 23:22:34 +11:00
try :
return " nim c " &
& " -d:CodexApiPort={apiPort} " &
& " -d:CodexDiscPort={discPort} " &
2025-01-14 14:56:30 +11:00
& " {strHardhatPort} " &
2025-01-10 23:22:34 +11:00
& " -d:TestId={test.testId} " &
& " {logging} " &
2024-12-20 16:23:40 +11:00
" --verbosity:0 " &
" --hints:off " &
" -d:release " &
" -r " &
2025-01-10 23:22:34 +11:00
& " {testFile} "
except ValueError as parent :
raiseTestManagerError " bad command -- \n " &
" , apiPort: " & $ apiPort &
" , discPort: " & $ discPort &
" , logging: " & logging &
" , testFile: " & testFile &
" , error: " & parent . msg ,
parent
proc runTest (
manager : TestManager ,
2025-01-14 14:56:30 +11:00
config : IntegrationTestConfig ) {. async : ( raises : [ CancelledError ] ) . } =
2024-12-20 16:23:40 +11:00
2025-01-10 23:22:34 +11:00
logScope :
config
trace " Running test "
var test = IntegrationTest (
2025-01-15 10:38:33 +11:00
manager : manager ,
2025-01-10 23:22:34 +11:00
config : config ,
testId : $ uint16 . example
)
2025-01-16 11:52:51 +11:00
test . timeStart = some Moment . now ( )
test . status = IntegrationTestStatus . Running
2025-01-14 14:56:30 +11:00
manager . tests . add test
2025-01-10 23:22:34 +11:00
2025-01-14 14:56:30 +11:00
var hardhat : Hardhat
var hardhatPort = int . none
var command : string
try :
if config . startHardhat :
2025-01-15 10:38:33 +11:00
hardhat = await test . startHardhat ( )
2025-01-14 14:56:30 +11:00
hardhatPort = hardhat . port . some
2025-01-15 10:38:33 +11:00
manager . hardhats . add hardhat
command = await test . buildCommand ( hardhatPort )
2025-01-14 14:56:30 +11:00
except TestManagerError as e :
error " Failed to start hardhat and build command " , error = e . msg
2025-01-16 11:52:51 +11:00
test . timeEnd = some Moment . now ( )
2025-01-14 14:56:30 +11:00
test . status = IntegrationTestStatus . Error
test . output = CommandExResponse . failure ( e )
2025-01-15 10:39:41 +11:00
test . printResult ( printStdOut = manager . debugHardhat or manager . debugCodexNodes ,
2025-01-14 17:48:24 +11:00
printStdErr = manager . debugTestHarness )
2025-01-14 14:56:30 +11:00
return
2025-01-10 23:22:34 +11:00
2025-01-15 10:39:41 +11:00
2025-01-10 23:22:34 +11:00
trace " Starting parallel integration test " , command
test . printStart ( )
2024-12-20 16:23:40 +11:00
test . process = execCommandEx (
command = command ,
2025-01-10 23:22:34 +11:00
timeout = manager . testTimeout
2024-12-20 16:23:40 +11:00
)
try :
2025-01-10 23:22:34 +11:00
let output = await test . process # waits on waitForExit
test . output = success ( output )
if output . status ! = 0 :
2025-01-13 14:30:18 +11:00
test . status = IntegrationTestStatus . Failed
2025-01-10 23:22:34 +11:00
else :
2025-01-13 14:30:18 +11:00
test . status = IntegrationTestStatus . Ok
2025-01-10 23:22:34 +11:00
2024-12-20 16:23:40 +11:00
except CancelledError as e :
raise e
2025-01-10 23:22:34 +11:00
2024-12-20 16:23:40 +11:00
except AsyncProcessTimeoutError as e :
2025-01-16 11:52:51 +11:00
test . timeEnd = some Moment . now ( )
error " Test timed out " , name = config . name , duration = test . duration
2024-12-20 16:23:40 +11:00
test . output = CommandExResponse . failure ( e )
2025-01-13 14:30:18 +11:00
test . status = IntegrationTestStatus . Timeout
2025-01-14 17:48:24 +11:00
test . printResult ( printStdOut = manager . debugCodexNodes ,
printStdErr = manager . debugTestHarness )
2025-01-10 23:22:34 +11:00
2024-12-20 16:23:40 +11:00
except AsyncProcessError as e :
2025-01-16 11:52:51 +11:00
test . timeEnd = some Moment . now ( )
error " Test failed to complete " , name = config . name , duration = test . duration
2024-12-20 16:23:40 +11:00
test . output = CommandExResponse . failure ( e )
2025-01-13 14:30:18 +11:00
test . status = IntegrationTestStatus . Error
2025-01-14 17:48:24 +11:00
test . printResult ( printStdOut = manager . debugCodexNodes ,
printStdErr = manager . debugTestHarness )
2024-12-20 16:23:40 +11:00
2025-01-14 14:56:30 +11:00
if config . startHardhat and not hardhat . isNil :
try :
trace " Stopping hardhat " , name = config . name
2025-01-15 10:38:33 +11:00
await hardhat . process . stop ( )
except CatchableError as e :
2025-01-14 14:56:30 +11:00
warn " Failed to stop hardhat node, continuing " ,
error = e . msg , test = test . config . name
if manager . debugHardhat :
test . printOutputMarker ( MarkerPosition . Start , " Hardhat stdout " )
for line in hardhat . output :
echo line
test . printOutputMarker ( MarkerPosition . Finish , " Hardhat stdout " )
manager . hardhats . keepItIf ( it ! = hardhat )
2025-01-16 11:52:51 +11:00
test . timeEnd = some Moment . now ( )
info " Test completed " , name = config . name , duration = test . duration
2025-01-14 17:48:24 +11:00
test . printResult ( printStdOut = manager . debugCodexNodes ,
printStdErr = manager . debugTestHarness )
2025-01-14 14:56:30 +11:00
2025-01-16 11:52:51 +11:00
proc continuallyShowUpdates ( manager : TestManager ) {. async : ( raises : [ ] ) . } =
try :
while true :
let sleepDuration = if manager . duration < 5 . minutes :
30 . seconds
else :
1 . minutes
if manager . tests . len > 0 :
echo " "
echoStyled styleBright , bgWhite , fgBlack ,
& " Integration tests status after {manager.duration} "
for test in manager . tests :
test . printResult ( )
await sleepAsync ( sleepDuration )
except CancelledError as e :
discard
2025-01-14 14:56:30 +11:00
proc runTests ( manager : TestManager ) {. async : ( raises : [ CancelledError ] ) . } =
var testFutures : seq [ Future [ void ] . Raising ( [ CancelledError ] ) ]
2024-12-20 16:23:40 +11:00
2025-01-16 11:52:51 +11:00
manager . timeStart = some Moment . now ( )
2024-12-20 16:23:40 +11:00
2025-01-14 17:48:24 +11:00
echoStyled styleBright , bgWhite , fgBlack ,
2025-01-13 14:30:18 +11:00
" \n [Integration Test Manager] Starting parallel integration tests "
2025-01-10 23:22:34 +11:00
2024-12-20 16:23:40 +11:00
for config in manager . configs :
testFutures . add manager . runTest ( config )
await allFutures testFutures
2025-01-16 11:52:51 +11:00
manager . timeEnd = some Moment . now ( )
2024-12-20 16:23:40 +11:00
2025-01-10 23:22:34 +11:00
proc withBorder (
msg : string ,
align = Align . Left ,
width = 67 ,
borders = { Border . Left , Border . Right } ) : string =
if borders . contains ( Border . Left ) :
result & = " | "
if align = = Align . Left :
result & = msg . alignLeft ( width )
elif align = = Align . Right :
result & = msg . align ( width )
if borders . contains ( Border . Right ) :
result & = " | "
proc printResult ( manager : TestManager ) {. raises : [ TestManagerError ] . } =
2024-12-20 16:23:40 +11:00
var successes = 0
var totalDurationSerial : Duration
for test in manager . tests :
2025-01-10 23:22:34 +11:00
totalDurationSerial + = test . duration
2025-01-13 14:30:18 +11:00
if test . status = = IntegrationTestStatus . Ok :
2025-01-10 23:22:34 +11:00
inc successes
2024-12-20 16:23:40 +11:00
# estimated time saved as serial execution with a single hardhat instance
# incurs less overhead
2025-01-10 23:22:34 +11:00
let relativeTimeSaved = ( ( totalDurationSerial - manager . duration ) . nanos * 100 ) div
( totalDurationSerial . nanos )
let passingStyle = if successes < manager . tests . len :
fgRed
else :
fgGreen
echo " \n ▢=====================================================================▢ "
2025-01-14 17:48:24 +11:00
echoStyled " | " , styleBright , styleUnderscore , " INTEGRATION TEST SUMMARY " , resetStyle , " " . withBorder ( Align . Right , 43 , { Border . Right } )
2025-01-10 23:22:34 +11:00
echo " " . withBorder ( )
2025-01-14 17:48:24 +11:00
echoStyled styleBright , " | TOTAL TIME : " , resetStyle , ( $ manager . duration ) . withBorder ( Align . Right , 49 , { Border . Right } )
echoStyled styleBright , " | TIME SAVED (EST): " , resetStyle , ( & " {relativeTimeSaved}% " ) . withBorder ( Align . Right , 49 , { Border . Right } )
echoStyled " | " , styleBright , passingStyle , " PASSING : " , resetStyle , passingStyle , ( & " {successes} / {manager.tests.len} " ) . align ( 49 ) , resetStyle , " | "
2024-12-20 16:23:40 +11:00
echo " ▢=====================================================================▢ "
2025-01-10 23:22:34 +11:00
proc start * ( manager : TestManager ) {. async : ( raises : [ CancelledError , TestManagerError ] ) . } =
2025-01-16 11:52:51 +11:00
manager . futContinuousUpdates = manager . continuallyShowUpdates ( )
asyncSpawn manager . futContinuousUpdates
2024-12-20 16:23:40 +11:00
await manager . runTests ( )
2025-01-10 23:22:34 +11:00
manager . printResult ( )
2024-12-20 16:23:40 +11:00
proc stop * ( manager : TestManager ) {. async : ( raises : [ CancelledError ] ) . } =
for test in manager . tests :
if not test . process . isNil and not test . process . finished :
await test . process . cancelAndWait ( )
for hardhat in manager . hardhats :
try :
2025-01-14 14:56:30 +11:00
await hardhat . process . stop ( )
2024-12-20 16:23:40 +11:00
except CatchableError as e :
2025-01-16 11:52:51 +11:00
trace " failed to stop hardhat node " , error = e . msg
await manager . futContinuousUpdates . cancelAndWait ( )