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
2025-01-22 19:38:58 +11:00
import pkg / codex / utils / trackedfutures
2024-12-20 16:23:40 +11:00
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
2025-01-20 12:54:57 +11:00
# Shows test status updates at regular time intervals. Useful for running
# locally while attended. Set to false for unattended runs, eg CI.
showContinuousStatusUpdates : bool
2025-01-16 11:52:02 +11:00
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-22 19:38:58 +11:00
trackedFutures : TrackedFutures
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-22 19:38:30 +11:00
New # Test not yet run
Running # Test currently running
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
2025-01-22 19:38:30 +11:00
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
2025-01-22 19:38:30 +11:00
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
2025-01-22 19:38:30 +11:00
Left
Right
2025-01-14 17:52:33 +11:00
Align {. pure . } = enum
2025-01-22 19:38:30 +11:00
Left
Right
2025-01-14 17:52:33 +11:00
MarkerPosition {. pure . } = enum
2025-01-22 19:38:30 +11:00
Start
2025-01-14 17:52:33 +11:00
Finish
2024-12-20 16:23:40 +11:00
{. push raises : [ ] . }
logScope :
topics = " testing integration testmanager "
2025-01-22 19:38:30 +11:00
proc printOutputMarker (
test : IntegrationTest , position : MarkerPosition , msg : string
) {. gcsafe , raises : [ ] . }
2025-01-15 10:39:41 +11:00
2025-01-22 19:38:30 +11:00
proc raiseTestManagerError (
msg : string , parent : ref CatchableError = nil
) {. raises : [ TestManagerError ] . } =
2025-01-10 23:22:34 +11:00
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 * (
2025-01-22 19:38:30 +11:00
_ : type TestManager ,
configs : seq [ IntegrationTestConfig ] ,
debugTestHarness = false ,
debugHardhat = false ,
debugCodexNodes = false ,
showContinuousStatusUpdates = 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 ,
2025-01-22 19:38:58 +11:00
testTimeout : testTimeout ,
trackedFutures : TrackedFutures . new ( ) ,
2024-12-20 16:23:40 +11:00
)
2025-01-13 14:39:07 +11:00
func init * (
2025-01-22 19:38:30 +11:00
_ : type IntegrationTestConfig , testFile : string , startHardhat : bool , name = " "
) : IntegrationTestConfig =
2025-01-13 14:39:07 +11:00
IntegrationTestConfig (
testFile : testFile ,
2025-01-22 19:38:30 +11:00
name : if name = = " " : testFile . extractFilename else : name ,
startHardhat : startHardhat ,
2025-01-13 14:39:07 +11:00
)
2024-12-20 16:23:40 +11:00
template withLock * ( lock : AsyncLock , body : untyped ) =
if lock . isNil :
lock = newAsyncLock ( )
await lock . acquire ( )
try :
body
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-22 19:38:30 +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 ) :
2025-01-20 16:02:44 +11:00
port = await nextFreePort ( test . manager . lastHardhatPort + 1 )
2025-01-15 10:38:33 +11:00
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 (
2025-01-22 19:38:30 +11:00
args , false , " hardhat for ' " & test . config . name & " ' " , onOutputLineCaptured
)
2025-01-15 10:40:12 +11:00
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-22 19:38:30 +11:00
proc printResult ( test : IntegrationTest , colour : ForegroundColor ) =
echoStyled styleBright ,
colour ,
& " [{toUpper $test .status}] " ,
resetStyle ,
test . config . name ,
resetStyle ,
styleDim ,
& " ({test.duration}) "
proc printOutputMarker ( test : IntegrationTest , position : MarkerPosition , msg : string ) =
2025-01-14 14:56:30 +11:00
if position = = MarkerPosition . Start :
echo " "
2025-01-13 14:38:26 +11:00
2025-01-22 19:38:30 +11:00
echoStyled styleBright ,
bgWhite , fgBlack , & " ----- {toUpper $position } {test.config.name} {msg} ----- "
2025-01-14 14:56:30 +11:00
if position = = MarkerPosition . Finish :
echo " "
2025-01-13 14:38:26 +11:00
2025-01-10 23:22:34 +11:00
proc printResult (
2025-01-22 19:38:30 +11:00
test : IntegrationTest ,
printStdOut = test . manager . debugCodexNodes ,
printStdErr = test . manager . debugTestHarness ,
) =
case test . status
2025-01-16 11:52:51 +11:00
of IntegrationTestStatus . New :
test . printResult ( fgBlue )
of IntegrationTestStatus . Running :
test . printResult ( fgCyan )
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-22 19:38:30 +11:00
test . printOutputMarker ( MarkerPosition . Start , " test harness errors (stderr) " )
2025-01-10 23:22:34 +11:00
echo output . stdError
2025-01-22 19:38:30 +11:00
test . printOutputMarker ( MarkerPosition . Finish , " test harness errors (stderr) " )
2025-01-14 17:48:24 +11:00
if printStdOut :
2025-01-22 19:38:30 +11:00
test . printOutputMarker ( MarkerPosition . Start , " codex node output (stdout) " )
2025-01-10 23:22:34 +11:00
echo output . stdOutput
2025-01-22 19:38:30 +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-22 19:38:30 +11:00
if printStdOut and output = ? test . output :
test . printOutputMarker ( MarkerPosition . Start , " codex node output (stdout) " )
2025-01-16 16:14:25 +11:00
echo output . stdOutput
2025-01-22 19:38:30 +11:00
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-22 19:38:30 +11:00
if printStdOut and output = ? test . output :
test . printOutputMarker ( MarkerPosition . Start , " codex node output (stdout) " )
2025-01-10 23:22:34 +11:00
echo output . stdOutput
2025-01-22 19:38:30 +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-22 19:38:30 +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 (
2025-01-22 19:38:30 +11:00
test : IntegrationTest , hardhatPort : ? int
) : Future [ string ] {. async : ( raises : [ CancelledError , TestManagerError ] ) . } =
let logging =
if not test . manager . debugTestHarness :
" "
else :
" -d:chronicles_log_level=TRACE " &
" -d:chronicles_disabled_topics=websock,JSONRPC-HTTP-CLIENT,JSONRPC-WS-CLIENT " &
" -d:chronicles_default_output_device=stdout " & " -d:chronicles_sinks=textlines "
2024-12-20 16:23:40 +11:00
2025-01-14 14:56:30 +11:00
let strHardhatPort =
2025-01-22 19:38:30 +11:00
if not test . config . startHardhat :
" "
2025-01-14 14:56:30 +11:00
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-22 19:38:30 +11:00
test . config . testFile , 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
2025-01-20 16:02:44 +11:00
withLock ( test . manager . codexPortLock ) :
2025-01-28 12:37:03 +11:00
# Increase the port by 20 to allow each test to run 20 codex nodes
2025-01-20 16:02:44 +11:00
# (clients, SPs, validators) giving a good chance the port will be free. We
# cannot rely on `nextFreePort` in multinodes entirely as there could be a
# concurrency issue where the port is determined free in mulitiple tests and
# then there is a clash during the run.
2025-01-28 12:37:03 +11:00
let apiPort = await nextFreePort ( test . manager . lastCodexApiPort + 20 )
2025-01-20 16:02:44 +11:00
test . manager . lastCodexApiPort = apiPort
2025-01-28 12:37:03 +11:00
let discPort = await nextFreePort ( test . manager . lastCodexDiscPort + 20 )
2025-01-20 16:02:44 +11:00
test . manager . lastCodexDiscPort = discPort
withLock ( test . manager . hardhatPortLock ) :
try :
2025-01-22 19:38:30 +11:00
return
" nim c " & & " -d:CodexApiPort={apiPort} " & & " -d:CodexDiscPort={discPort} " &
& " {strHardhatPort} " & & " -d:TestId={test.testId} " & & " {logging} " &
" --verbosity:0 " & " --hints:off " & " -d:release " & " -r " & & " {testFile} "
2025-01-20 16:02:44 +11:00
except ValueError as parent :
2025-01-22 19:38:30 +11:00
raiseTestManagerError " bad command -- \n " & " , apiPort: " & $ apiPort &
" , discPort: " & $ discPort & " , logging: " & logging & " , testFile: " &
testFile & " , error: " & parent . msg , parent
2025-01-10 23:22:34 +11:00
2025-01-22 19:38:30 +11:00
proc setup (
test : IntegrationTest
) : Future [ ? Hardhat ] {. async : ( raises : [ CancelledError , TestManagerError ] ) . } =
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 (
2025-01-22 19:38:30 +11:00
test : IntegrationTest , hardhat : ? Hardhat
) {. async : ( raises : [ CancelledError ] ) . } =
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-22 19:38:30 +11:00
test . manager . hardhats . keepItIf ( it ! = hardhat )
2025-01-16 16:14:25 +11:00
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 ( )
2025-01-22 19:38:30 +11:00
test . process =
execCommandEx ( command = test . command , timeout = test . manager . testTimeout )
2025-01-16 16:14:25 +11:00
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 :
2025-01-22 19:38:30 +11:00
let sleepDuration = if manager . duration < 5 . minutes : 30 . seconds else : 1 . minutes
2025-01-16 11:52:51 +11:00
if manager . tests . len > 0 :
echo " "
2025-01-22 19:38:30 +11:00
echoStyled styleBright ,
bgWhite , fgBlack , & " Integration tests status after {manager.duration} "
2025-01-16 11:52:51 +11:00
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-22 19:38:30 +11:00
proc untilTimeout (
fut : Future [ void ] , timeout : Duration
) : Future [ bool ] {. async : ( raises : [ CancelledError ] ) . } =
2025-01-16 16:14:25 +11:00
# 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
2025-01-22 19:38:30 +11:00
let e = newException (
AsyncProcessTimeoutError , " Test did not complete before elapsed timeout "
)
2025-01-16 16:14:25 +11:00
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-22 19:38:30 +11:00
echoStyled styleBright ,
bgWhite , fgBlack , " \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-22 19:38:30 +11:00
var test =
IntegrationTest ( manager : manager , config : config , testId : $ uint16 . example )
2025-01-16 16:14:25 +11:00
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 (
2025-01-22 19:38:30 +11:00
msg : string , align = Align . Left , width = 67 , borders = { Border . Left , Border . Right }
) : string =
2025-01-10 23:22:34 +11:00
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 & = " | "
2025-01-22 19:38:30 +11:00
proc printResult ( manager : TestManager ) {. raises : [ TestManagerError ] . } =
2024-12-20 16:23:40 +11:00
var successes = 0
var totalDurationSerial : Duration
2025-01-22 19:38:30 +11:00
let showSummary =
manager . debugCodexNodes or manager . debugHardhat or manager . debugTestHarness
2025-01-16 16:14:25 +11:00
if showSummary :
echo " "
2025-01-22 19:38:30 +11:00
echoStyled styleBright ,
styleUnderscore , bgWhite , fgBlack , & " INTEGRATION TESTS RESULT "
2025-01-16 16:14:25 +11:00
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-22 19:38:30 +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-22 19:38:30 +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-22 19:38:30 +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-22 19:38:30 +11:00
proc start * (
manager : TestManager
) {. async : ( raises : [ CancelledError , TestManagerError ] ) . } =
if manager . showContinuousStatusUpdates :
let fut = manager . continuallyShowUpdates ( )
manager . trackedFutures . track fut
asyncSpawn fut
2025-01-16 16:14:25 +11:00
2024-12-20 16:23:40 +11:00
await manager . runTests ( )
2025-01-16 16:14:25 +11:00
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 ] ) . } =
2025-01-22 19:38:58 +11:00
await manager . trackedFutures . cancelTracked ( )
2024-12-20 16:23:40 +11:00
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-22 19:38:30 +11:00
trace " failed to stop hardhat node " , error = e . msg