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
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
2025-01-16 16:14:25 +11:00
command : string
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-16 16:14:25 +11:00
printStdOut = test . manager . debugCodexNodes ,
printStdErr = test . manager . debugTestHarness ) =
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-16 16:14:25 +11:00
if printStdOut and
output = ? test . output :
test . printOutputMarker ( MarkerPosition . Start ,
" codex node output (stdout) " )
echo output . stdOutput
test . printOutputMarker ( MarkerPosition . Finish ,
" codex node output (stdout) " )
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
2025-01-16 16:14:25 +11:00
proc setup (
test : IntegrationTest ) : Future [ ? Hardhat ] {. async : ( raises : [ CancelledError , TestManagerError ] ) . } =
2025-01-10 23:22:34 +11:00
2025-01-16 16:14:25 +11:00
var hardhat = Hardhat . none
2025-01-14 14:56:30 +11:00
var hardhatPort = int . none
2024-12-20 16:23:40 +11:00
2025-01-16 16:14:25 +11:00
if test . config . startHardhat :
let hh = await test . startHardhat ( )
hardhat = some hh
hardhatPort = some hh . port
test . manager . hardhats . add hh
2025-01-10 23:22:34 +11:00
2025-01-16 16:14:25 +11:00
test . command = await test . buildCommand ( hardhatPort )
2025-01-10 23:22:34 +11:00
2025-01-16 16:14:25 +11:00
return hardhat
2025-01-10 23:22:34 +11:00
2025-01-16 16:14:25 +11:00
proc teardown (
test : IntegrationTest ,
hardhat : ? Hardhat ) {. async : ( raises : [ CancelledError ] ) . } =
2025-01-10 23:22:34 +11:00
2025-01-16 16:14:25 +11:00
if test . config . startHardhat and hardhat = ? hardhat :
2025-01-14 14:56:30 +11:00
try :
2025-01-16 16:14:25 +11:00
trace " Stopping hardhat " , name = test . 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
2025-01-16 16:14:25 +11:00
if test . manager . debugHardhat :
2025-01-14 14:56:30 +11:00
test . printOutputMarker ( MarkerPosition . Start , " Hardhat stdout " )
for line in hardhat . output :
echo line
test . printOutputMarker ( MarkerPosition . Finish , " Hardhat stdout " )
2025-01-16 16:14:25 +11:00
test . manager . hardhats . keepItIf ( it ! = hardhat )
proc start ( test : IntegrationTest ) {. async : ( raises : [ ] ) . } =
logScope :
config = test . config
trace " Running test "
test . timeStart = some Moment . now ( )
test . status = IntegrationTestStatus . Running
var hardhat = none Hardhat
try :
try :
hardhat = await test . setup ( )
except TestManagerError as e :
error " Failed to start hardhat and build command " , error = e . msg
test . timeEnd = some Moment . now ( )
test . status = IntegrationTestStatus . Error
test . output = CommandExResponse . failure ( e )
return
try :
trace " Starting parallel integration test " , command = test . command
test . printStart ( )
test . process = execCommandEx (
command = test . command ,
timeout = test . manager . testTimeout
)
let output = await test . process # waits on waitForExit
test . output = success ( output )
if output . status ! = 0 :
test . status = IntegrationTestStatus . Failed
else :
test . status = IntegrationTestStatus . Ok
except AsyncProcessTimeoutError as e :
test . timeEnd = some Moment . now ( )
error " Test timed out " , name = test . config . name , duration = test . duration
test . output = CommandExResponse . failure ( e )
test . status = IntegrationTestStatus . Timeout
except AsyncProcessError as e :
test . timeEnd = some Moment . now ( )
error " Test failed to complete " , name = test . config . name , duration = test . duration
test . output = CommandExResponse . failure ( e )
test . status = IntegrationTestStatus . Error
await test . teardown ( hardhat )
except CancelledError :
discard # start is asyncSpawned, do not propagate
2025-01-14 14:56:30 +11:00
2025-01-16 11:52:51 +11:00
test . timeEnd = some Moment . now ( )
2025-01-16 16:14:25 +11:00
if test . status = = IntegrationTestStatus . Ok :
info " Test completed " , name = test . config . name , duration = test . duration
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 :
2025-01-16 16:14:25 +11:00
test . printResult ( false , false )
if manager . tests . len > 0 :
echo " "
2025-01-16 11:52:51 +11:00
await sleepAsync ( sleepDuration )
except CancelledError as e :
discard
2025-01-16 16:14:25 +11:00
proc untilTimeout ( fut : Future [ void ] , timeout : Duration ) : Future [ bool ] {. async : ( raises : [ CancelledError ] ) . } =
# workaround for withTimeout, which did not work correctly
try :
let timer = sleepAsync ( timeout )
return ( await race ( fut , timer ) ) = = fut
except ValueError :
discard
proc run ( test : IntegrationTest ) {. async : ( raises : [ ] ) . } =
try :
let futStart = test . start ( )
let completedBeforeTimeout = await futStart . untilTimeout ( test . manager . testTimeout )
if not completedBeforeTimeout :
test . timeEnd = some Moment . now ( )
error " Test timed out " , name = test . config . name , duration = test . duration
let e = newException ( AsyncProcessTimeoutError ,
" Test did not complete before elapsed timeout " )
test . output = CommandExResponse . failure ( e )
test . status = IntegrationTestStatus . Timeout
if not futStart . finished :
await futStart . cancelAndWait ( )
test . printResult ( )
except CancelledError :
discard # do not propagate due to asyncSpawn
2025-01-14 14:56:30 +11:00
proc runTests ( manager : TestManager ) {. async : ( raises : [ CancelledError ] ) . } =
2025-01-16 16:14:25 +11:00
var testFutures : seq [ Future [ void ] ]
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 :
2025-01-16 16:14:25 +11:00
var test = IntegrationTest (
manager : manager ,
config : config ,
testId : $ uint16 . example
)
manager . tests . add test
let futRun = test . run ( )
testFutures . add futRun
asyncSpawn futRun
2024-12-20 16:23:40 +11:00
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
2025-01-16 16:14:25 +11:00
let showSummary = manager . debugCodexNodes or manager . debugHardhat or manager . debugTestHarness
if showSummary :
echo " "
echoStyled styleBright , styleUnderscore , bgWhite , fgBlack ,
& " INTEGRATION TESTS RESULT "
2024-12-20 16:23:40 +11:00
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
2025-01-16 16:14:25 +11:00
# because debug output can really make things hard to read, show a nice
# summary of test results
if showSummary :
test . printResult ( false , false )
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
2025-01-16 16:14:25 +11:00
2025-01-10 23:22:34 +11:00
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 16:14:25 +11:00
let futContinuousUpdates = manager . continuallyShowUpdates ( )
asyncSpawn futContinuousUpdates
2024-12-20 16:23:40 +11:00
await manager . runTests ( )
2025-01-16 16:14:25 +11:00
await futContinuousUpdates . cancelAndWait ( )
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 16:14:25 +11:00
trace " failed to stop hardhat node " , error = e . msg