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-02-05 18:31:40 +11:00
from std / times import fromUnix , format , now
2025-01-13 14:38:26 +11:00
from std / unicode import toUpper
2024-12-20 16:23:40 +11:00
import pkg / chronos
import pkg / chronos / asyncproc
2025-03-05 15:16:07 +11:00
import pkg / codex / conf
2024-12-20 16:23:40 +11:00
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
port : int
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
TestManagerConfig * = object # Echoes stdout from Hardhat process
debugHardhat * : bool
2025-03-14 16:56:34 +11:00
# Shows all log topics at TRACE log level by disabling codex node output log
# topic filters, eg libp2p, websock, JSON RPC
noCodexLogFilters * : bool
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +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
logsDir * : string
testTimeout * : Duration # individual test timeout
2024-12-20 16:23:40 +11:00
TestManager * = ref object
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
config : TestManagerConfig
testConfigs : seq [ IntegrationTestConfig ]
2024-12-20 16:23:40 +11:00
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
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-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-02-28 22:42:01 +11:00
process : AsyncProcessRef
2025-01-16 11:52:51 +11:00
timeStart : ? Moment
timeEnd : ? Moment
2025-02-28 22:42:01 +11:00
output : ? ! TestOutput
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
2025-02-05 18:31:40 +11:00
logsDir : string
2024-12-20 16:23:40 +11:00
2025-02-28 22:42:01 +11:00
TestOutput = ref object
2025-03-19 12:59:06 +11:00
stdOut * : seq [ string ]
stdErr * : seq [ string ]
2025-02-28 22:42:01 +11:00
exitCode * : ? int
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-02-04 12:35:15 +11:00
template ignoreCancelled ( body ) =
try :
body
except CancelledError :
discard
2025-03-05 15:16:07 +11:00
func logFile * ( _ : type TestManager , dir : string ) : string =
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
dir / " testmanager.chronicles.log "
2025-03-05 15:16:07 +11:00
func logFile ( manager : TestManager ) : string =
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
TestManager . logFile ( manager . config . logsDir )
2025-03-05 15:16:07 +11:00
func logFile ( test : IntegrationTest , fileName : string ) : string =
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
let testName = sanitize ( test . config . name )
test . logsDir / & " {testName}.{fileName} "
func isErrorLike ( output : ? ! TestOutput ) : bool =
# Three lines is an arbitrary number, however it takes into account the
# "LevelDB already build" line and blank line that is output to stdout. This
# typically means that the exitCode == 1 (test failed) and if stdout is short,
# we're dealing with an error
2025-03-19 12:59:06 +11:00
o = ? output and o . stdOut . len < 3
2025-03-05 15:16:07 +11:00
2025-01-10 23:22:34 +11:00
proc new * (
2025-01-22 19:38:30 +11:00
_ : type TestManager ,
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
config : TestManagerConfig ,
testConfigs : seq [ IntegrationTestConfig ] ,
2025-01-22 19:38:30 +11:00
) : TestManager =
2024-12-20 16:23:40 +11:00
TestManager (
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
config : config ,
testConfigs : testConfigs ,
2024-12-20 16:23:40 +11:00
lastHardhatPort : 8545 ,
lastCodexApiPort : 8000 ,
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
lastCodexDiscPort : 18000 , # keep separated by 10000 to minimise overlap
2025-01-22 19:38:58 +11:00
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
2025-01-28 20:49:12 +11:00
proc allTestsPassed * ( manager : TestManager ) : ? ! bool =
2025-01-28 18:58:43 +11:00
for test in manager . tests :
if test . status in { IntegrationTestStatus . New , IntegrationTestStatus . Running } :
return failure " Integration tests not complete "
if test . status ! = IntegrationTestStatus . Ok :
2025-01-28 20:49:12 +11:00
return success false
2025-01-28 18:58:43 +11:00
2025-01-28 20:49:12 +11:00
return success true
2025-01-28 18:58:43 +11:00
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 ( )
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 )
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
if test . manager . config . debugHardhat :
2025-02-05 18:31:40 +11:00
args . add ( " --log-file= " & test . logsDir / " hardhat.log " )
2024-12-20 16:23:40 +11:00
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-03-24 15:40:13 +11:00
args , false , " hardhat for ' " & test . config . name & " ' "
2025-01-22 19:38:30 +11:00
)
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 :
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-02-25 17:54:43 +11:00
proc colorise ( output : string ) : string =
proc setColour ( text : string , colour : ForegroundColor ) : string =
& " {ansiForegroundColorCode(colour, true)}{text}{ansiResetCode} "
let replacements = @ [ ( " [OK] " , fgGreen ) , ( " [FAILED] " , fgRed ) , ( " [Suite] " , fgBlue ) ]
result = output
for ( text , colour ) in replacements :
result = result . replace ( text , text . setColour ( colour ) )
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
proc printResult ( test : IntegrationTest , printStdOut , printStdErr : bool ) =
2025-01-22 19:38:30 +11:00
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 " )
2025-01-10 23:22:34 +11:00
if output = ? test . output :
2025-03-04 13:11:38 +11:00
if printStdErr :
2025-02-05 18:31:40 +11:00
test . printOutputMarker ( MarkerPosition . Start , " test file errors (stderr) " )
2025-03-19 12:59:06 +11:00
echo output . stdErr . join ( " \n " )
2025-02-05 18:31:40 +11:00
test . printOutputMarker ( MarkerPosition . Finish , " test file errors (stderr) " )
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
of IntegrationTestStatus . Failed :
if output = ? test . output :
2025-03-07 15:48:52 +11:00
if printStdErr :
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
test . printOutputMarker ( MarkerPosition . Start , " test file errors (stderr) " )
2025-03-19 12:59:06 +11:00
echo output . stdErr . join ( " \n " )
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
test . printOutputMarker ( MarkerPosition . Finish , " test file errors (stderr) " )
2025-03-04 13:11:38 +11:00
if printStdOut :
test . printOutputMarker ( MarkerPosition . Start , " codex node output (stdout) " )
2025-03-19 12:59:06 +11:00
echo output . stdOut . join ( " \n " ) . colorise
2025-03-04 13:11:38 +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-03-19 12:59:06 +11:00
echo output . stdOut . join ( " \n " ) . colorise
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-03-19 12:59:06 +11:00
echo output . stdOut . join ( " \n " ) . colorise
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 ] ) . } =
2025-02-05 18:31:40 +11:00
var hhPort = string . none
if test . config . startHardhat :
without port = ? hardhatPort :
raiseTestManagerError " hardhatPort required when ' config.startHardhat ' is true "
hhPort = some " -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-02-06 18:07:33 +11:00
# Increase the port by 1000 to allow each test to run 1000 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
2025-02-06 18:07:33 +11:00
# then there is a clash during the run. Windows, in particular, does not
# like giving up ports.
2025-02-06 17:50:33 +11:00
let apiPort = await nextFreePort ( test . manager . lastCodexApiPort + 1000 )
2025-01-20 16:02:44 +11:00
test . manager . lastCodexApiPort = apiPort
2025-02-06 17:50:33 +11:00
let discPort = await nextFreePort ( test . manager . lastCodexDiscPort + 1000 )
2025-01-20 16:02:44 +11:00
test . manager . lastCodexDiscPort = discPort
2025-03-14 16:56:34 +11:00
let codexLogLevel =
if test . manager . config . noCodexLogFilters :
" TRACE "
else :
" TRACE;disabled:libp2p,websock,JSONRPC-HTTP-CLIENT,JSONRPC-WS-CLIENT,discv5 "
2025-01-20 16:02:44 +11:00
withLock ( test . manager . hardhatPortLock ) :
try :
2025-01-22 19:38:30 +11:00
return
2025-02-05 18:31:40 +11:00
#!fmt: off
" nim c " &
& " -d:CodexApiPort={apiPort} " &
& " -d:CodexDiscPort={discPort} " &
2025-03-14 16:56:34 +11:00
& " -d:CodexLogsDir={test.logsDir} " &
& " -d:CodexLogLevel= \" {codexLogLevel} \" " &
& " -d:CodexLogToFile=true " &
2025-02-05 18:31:40 +11:00
( hhPort | ? " " ) & " " &
& " -d:TestId={test.testId} " &
2025-03-14 16:56:34 +11:00
# Log multinodes chronicles logs settings (log to file with no
# colours, and loglevel = TRACE).
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
" -d:chronicles_log_level=TRACE " &
" -d:chronicles_sinks=textlines[nocolors,file] " &
2025-03-26 21:02:55 +11:00
" -d:nimUnittestOutputLevel:VERBOSE " &
2025-02-05 18:31:40 +11:00
" --verbosity:0 " &
" --hints:off " &
" -d:release " &
" -r " &
& " {testFile} "
#!fmt: on
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 &
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
" , discPort: " & $ discPort & " , 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-03-04 13:11:38 +11:00
proc teardownHardhat ( test : IntegrationTest , hardhat : Hardhat ) {. async : ( raises : [ ] ) . } =
try :
trace " Stopping hardhat " , name = test . config . name
await noCancel hardhat . process . stop ( )
trace " Hardhat stopped " , name = test . config . name
except CatchableError as e : # CancelledError not raised due to noCancel
warn " Failed to stop hardhat node, continuing " ,
error = e . msg , test = test . config . name
test . manager . hardhats . keepItIf ( it ! = hardhat )
2025-03-24 19:39:38 +11:00
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 :
2025-03-24 19:56:52 +11:00
let cmdResult = await forceKillProcess ( " nim.exe " , & " -d:TestId {test.testId} " )
2025-03-24 19:39:38 +11:00
if cmdResult . status > 0 :
error " Failed to forcefully kill windows test process " ,
2025-03-24 19:56:52 +11:00
testId = test . testId , exitCode = cmdResult . status , stderr = cmdResult . stdError
2025-03-24 19:39:38 +11:00
else :
trace " Successfully killed windows test process by force " ,
2025-03-24 19:56:52 +11:00
testId = test . testId ,
exitCode = cmdResult . status ,
stdout = cmdResult . stdOutput
2025-03-24 19:39:38 +11:00
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 :
2025-03-24 19:56:52 +11:00
error " Failed to forcefully kill windows test process " ,
testId = test . testId , error = e . msg
2025-03-24 19:39:38 +11:00
except AsyncProcessTimeoutError as e :
error " Timeout while forcefully killing windows test process " ,
2025-03-24 19:56:52 +11:00
testId = test . testId , error = e . msg
2025-03-24 19:39:38 +11:00
2025-03-19 12:59:06 +11:00
proc teardownTest ( test : IntegrationTest ) {. async : ( raises : [ ] ) . } =
2025-03-07 15:48:52 +11:00
logScope :
test = test . config . name
trace " Tearing down test "
2025-03-04 13:11:38 +11:00
test . timeEnd = some Moment . now ( )
if not test . process . isNil :
var output = test . output . expect ( " should have output value " )
if test . process . running | ? false :
2025-03-21 11:23:21 +11:00
trace " Test process still running, terminating... "
2025-03-04 13:11:38 +11:00
try :
output . exitCode =
2025-03-18 15:48:29 +11:00
some ( await noCancel test . process . terminateAndWaitForExit ( 1 . seconds ) )
2025-03-04 13:11:38 +11:00
trace " Test process terminated " , exitCode = output . exitCode
except AsyncProcessError , AsyncProcessTimeoutError :
2025-03-07 15:48:52 +11:00
let e = getCurrentException ( )
warn " Test process failed to terminate, check for zombies " , error = e . msg
2025-03-04 13:11:38 +11:00
2025-03-24 19:39:38 +11:00
await test . closeProcessStreams ( )
test . process = nil
2025-03-04 13:11:38 +11:00
2025-03-19 12:59:06 +11:00
proc teardown ( test : IntegrationTest , hardhat : ? Hardhat ) {. async : ( raises : [ ] ) . } =
2025-03-04 13:11:38 +11:00
if test . config . startHardhat and hardhat = ? hardhat and not hardhat . process . isNil :
await test . teardownHardhat ( hardhat )
2025-01-14 14:56:30 +11:00
2025-03-19 12:59:06 +11:00
await test . teardownTest ( )
2025-01-16 16:14:25 +11:00
2025-02-04 12:35:15 +11:00
proc untilTimeout (
2025-02-28 22:42:01 +11:00
fut : InternalRaisesFuture , timeout : Duration
) : Future [ void ] {. async : ( raises : [ CancelledError , AsyncTimeoutError ] ) . } =
## Returns a Future that completes when either fut finishes or timeout elapses,
## or if they finish at the same time. If timeout elapses, an AsyncTimeoutError
## is raised. If fut fails, its error is raised.
2025-02-04 12:35:15 +11:00
let timer = sleepAsync ( timeout )
2025-02-28 22:42:01 +11:00
defer :
2025-03-04 13:11:38 +11:00
# Called even when exception raised, including CancelledError. `race` does
# not cancel its futures when it's cancelled, so cancel here, which is ok
# even if they're already completed.
2025-02-28 22:42:01 +11:00
await fut . cancelAndWait ( )
await timer . cancelAndWait ( )
try :
discard await race ( fut , timer )
2025-02-04 12:35:15 +11:00
except ValueError as e :
2025-02-28 22:42:01 +11:00
raiseAssert " should not happen "
if fut . finished ( ) : # or fut and timer both finished simultaneously
if fut . failed ( ) :
await fut # raise fut error
return # unreachable, for readability
else : # timeout
raise newException ( AsyncTimeoutError , " Timed out " )
2025-02-04 12:35:15 +11:00
2025-03-19 12:59:06 +11:00
proc captureOutput (
2025-03-21 11:23:21 +11:00
process : AsyncProcessRef , stream : AsyncStreamReader , filePath : string
2025-03-19 12:59:06 +11:00
) : Future [ seq [ string ] ] {. async : ( raises : [ CancelledError ] ) . } =
var output : seq [ string ] = @ [ ]
try :
while process . running . option = = some true :
while ( let line = await stream . readLine ( 0 , " \n " ) ; line ! = " " ) :
2025-03-21 11:23:21 +11:00
try :
output . add line
2025-03-21 12:18:26 +11:00
filePath . appendFile ( line & " \n " . stripAnsi )
2025-03-21 11:23:21 +11:00
await sleepAsync ( 1 . nanos )
except IOError as e :
warn " Failed to write test stdout and/or stderr to file " , error = e . msg
2025-03-19 12:59:06 +11:00
await sleepAsync ( 1 . nanos )
return output
except CancelledError as e :
raise e
except AsyncStreamError as e :
error " Error reading output stream " , error = e . msg
proc captureProcessOutput (
test : IntegrationTest
) : Future [ ( seq [ string ] , seq [ string ] ) ] {. async : ( raises : [ CancelledError ] ) . } =
logScope :
name = test . config . name
trace " Reading stdout and stderr streams from test process "
2025-03-21 11:23:21 +11:00
let futStdOut =
test . process . captureOutput ( test . process . stdoutStream , test . logFile ( " stdout.log " ) )
let futStdErr =
test . process . captureOutput ( test . process . stderrStream , test . logFile ( " stderr.log " ) )
2025-03-19 12:59:06 +11:00
await allFutures ( futStdOut , futStdErr )
return ( await futStdOut , await futStdErr )
2025-01-16 16:14:25 +11:00
proc start ( test : IntegrationTest ) {. async : ( raises : [ ] ) . } =
logScope :
2025-02-28 22:42:01 +11:00
name = test . config . name
duration = test . duration
2025-01-16 16:14:25 +11:00
trace " Running test "
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
test . logsDir = test . manager . config . logsDir / sanitize ( test . config . name )
try :
createDir ( test . logsDir )
except CatchableError as e :
test . timeStart = some Moment . now ( )
test . timeEnd = some Moment . now ( )
test . status = IntegrationTestStatus . Error
test . output = TestOutput . failure ( e )
error " failed to create test log dir " , logDir = test . logsDir , error = e . msg
return
2025-02-05 18:31:40 +11:00
2025-01-16 16:14:25 +11:00
test . timeStart = some Moment . now ( )
test . status = IntegrationTestStatus . Running
var hardhat = none Hardhat
2025-02-04 12:35:15 +11:00
ignoreCancelled :
2025-01-16 16:14:25 +11:00
try :
hardhat = await test . setup ( )
except TestManagerError as e :
test . timeEnd = some Moment . now ( )
test . status = IntegrationTestStatus . Error
2025-02-28 22:42:01 +11:00
test . output = TestOutput . failure ( e )
error " Failed to start hardhat and build command " , error = e . msg
2025-01-16 16:14:25 +11:00
return
2025-02-28 22:42:01 +11:00
trace " Starting parallel integration test " ,
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
command = test . command , timeout = test . manager . config . testTimeout
2025-02-28 22:42:01 +11:00
test . printStart ( )
2025-03-19 12:59:06 +11:00
2025-01-16 16:14:25 +11:00
try :
2025-02-28 22:42:01 +11:00
test . process = await startProcess (
command = test . command ,
options = { AsyncProcessOption . EvalCommand } ,
stdoutHandle = AsyncProcess . Pipe ,
stderrHandle = AsyncProcess . Pipe ,
)
except AsyncProcessError as e :
test . timeEnd = some Moment . now ( )
error " Failed to start test process " , error = e . msg
test . output = TestOutput . failure ( e )
test . status = IntegrationTestStatus . Error
return
2025-02-04 12:35:15 +11:00
2025-03-19 15:15:18 +11:00
var futCaptureOutput : Future [ ( seq [ string ] , seq [ string ] ) ] . Raising ( [ CancelledError ] )
2025-03-04 13:11:38 +11:00
defer :
# called at the end of successful runs but also when `start` is cancelled
# (from `untilTimeout`) due to a timeout. This defer runs first before
# `untilTimeout` exceptions are handled in `run`
2025-03-19 12:59:06 +11:00
await test . teardown ( hardhat ) # doesn't raise CancelledError, so noCancel not needed
await futCaptureOutput . cancelAndWait ( )
var output = TestOutput . new ( )
test . output = success ( output )
2025-03-19 15:15:18 +11:00
futCaptureOutput = test . captureProcessOutput ( )
2025-03-04 13:11:38 +11:00
2025-02-28 22:42:01 +11:00
output . exitCode =
try :
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
some ( await test . process . waitForExit ( test . manager . config . testTimeout ) )
2025-02-28 22:42:01 +11:00
except AsyncProcessTimeoutError as e :
test . timeEnd = some Moment . now ( )
test . status = IntegrationTestStatus . Timeout
error " Test process failed to exit before timeout " ,
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
timeout = test . manager . config . testTimeout
2025-02-28 22:42:01 +11:00
return
except AsyncProcessError as e :
test . timeEnd = some Moment . now ( )
test . status = IntegrationTestStatus . Error
test . output = TestOutput . failure ( e )
error " Test failed to complete " , error = e . msg
return
2025-03-19 12:59:06 +11:00
let ( stdOut , stdErr ) = await futCaptureOutput
output . stdOut = stdOut
output . stdErr = stdErr
2025-02-28 22:42:01 +11:00
test . status =
if output . exitCode = = some QuitSuccess :
IntegrationTestStatus . Ok
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
elif output . exitCode = = some QuitFailure :
2025-02-28 22:42:01 +11:00
IntegrationTestStatus . Failed
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
else :
IntegrationTestStatus . Error
2025-02-28 22:42:01 +11:00
2025-01-16 11:52:51 +11:00
proc continuallyShowUpdates ( manager : TestManager ) {. async : ( raises : [ ] ) . } =
2025-02-04 12:35:15 +11:00
ignoreCancelled :
2025-01-16 11:52:51 +11:00
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-03-04 13:11:38 +11:00
test . printSummary ( )
2025-01-16 16:14:25 +11:00
if manager . tests . len > 0 :
echo " "
2025-01-16 11:52:51 +11:00
await sleepAsync ( sleepDuration )
2025-01-16 16:14:25 +11:00
proc run ( test : IntegrationTest ) {. async : ( raises : [ ] ) . } =
2025-02-04 12:35:15 +11:00
ignoreCancelled :
2025-02-28 22:42:01 +11:00
let futStart = test . start ( )
# await futStart
try :
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
await futStart . untilTimeout ( test . manager . config . testTimeout )
2025-02-28 22:42:01 +11:00
except AsyncTimeoutError :
test . timeEnd = some Moment . now ( )
test . status = IntegrationTestStatus . Timeout
2025-03-04 13:11:38 +11:00
# futStart will be cancelled by untilTimeout and that will run the
# teardown procedure (in defer)
2025-02-28 22:42:01 +11:00
2025-03-04 13:11:38 +11:00
test . printResult (
printStdOut = test . status ! = IntegrationTestStatus . Ok ,
2025-03-07 15:48:52 +11:00
printStdErr =
test . status = = IntegrationTestStatus . Error or
( test . status = = IntegrationTestStatus . Failed and test . output . isErrorLike ) ,
2025-03-04 13:11:38 +11:00
)
2025-03-05 15:16:07 +11:00
logScope :
name = test . config . name
duration = test . duration
2025-03-18 15:48:29 +11:00
doAssert test . timeEnd . isSome , " Integration test end time not set! "
doAssert ( test . output . isOk and output = ? test . output and output ! = nil ) or
test . output . isErr , " Integration test output not set! "
2025-03-05 15:16:07 +11:00
case test . status
of IntegrationTestStatus . New :
raiseAssert " Test has completed, but is in the New state "
of IntegrationTestStatus . Running :
raiseAssert " Test has completed, but is in the Running state "
of IntegrationTestStatus . Error :
error " Test errored " ,
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
exitCode = test . output . option . ? exitCode ,
2025-03-05 15:16:07 +11:00
error = test . output . errorOption . ? msg ,
stack = test . output . errorOption . ? getStackTrace ( )
of IntegrationTestStatus . Failed :
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
error " Test failed " , exitCode = test . output . option . ? exitCode
2025-03-05 15:16:07 +11:00
of IntegrationTestStatus . Timeout :
error " Test timed out "
of IntegrationTestStatus . Ok :
notice " Test passed "
2025-01-16 16:14:25 +11:00
2025-03-14 16:57:22 +11:00
proc runTests ( manager : TestManager ) {. async : ( raises : [ ] ) . } =
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 "
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
notice " [Integration Test Manager] Starting parallel integration tests " ,
config = manager . config
2025-01-10 23:22:34 +11:00
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
for config in manager . testConfigs :
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
2024-12-20 16:23:40 +11:00
2025-03-14 16:57:22 +11:00
try :
await allFutures testFutures
manager . timeEnd = some Moment . now ( )
except CancelledError as e :
discard
finally :
2025-01-31 16:21:55 +11:00
for fut in testFutures :
2025-02-28 22:42:01 +11:00
await fut . cancelAndWait ( )
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-03-20 13:39:09 +11:00
proc printResult ( manager : TestManager ) =
2024-12-20 16:23:40 +11:00
var successes = 0
var totalDurationSerial : Duration
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
echo " "
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
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
test . printSummary ( )
2025-01-16 16:14:25 +11:00
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-03-05 15:16:07 +11:00
notice " INTEGRATION TEST SUMMARY " ,
totalTime = manager . duration ,
timeSavedEst = & " {relativeTimeSaved}% " ,
passing = & " {successes} / {manager.tests.len} "
2024-12-20 16:23:40 +11:00
2025-03-21 11:23:21 +11:00
proc start * ( manager : TestManager ) {. async : ( raises : [ CancelledError ] ) . } =
refactor: debug and logging
- Move test manager values to config object
- Increase codex port separation (between api and disc ports) in an attempt to prevent overlap across tests (ie Test1: api=8000(tcp), disc=9000(udp), and Test2: api=9000(tcp), disc=10000(udp))
- print stderr when exitcode == 1 if there's < 3 lines of stdout
- Logging:
- Always write test manager harness chronicles logs to file, ie testmanager.chronicles.log in the root of the `integration/logs/<run name>` dir
- Always write individual test stdout to file, ie `<test file name>.stdout.log` in the root of the `integration/logs/<run name>/<test file name>` dir
- On error, print stderr to screen and write stderr to file. Or on failure, if stdout is sufficiently short, write stderr to screen and file in `integration/logs/<run name>/<test file name>/<test file name>.stderr.log`
- When debugging, ie DebugCodexNodes == true
- Removes DebugTestHarness from controlling anything other than printing chronicles output from the testmanager to the terminal
- Now, if DebugCodexNodes is set to true:
- Codex node (chronicles) output for multinodesuite tests is logged to file, eg `integration/logs/<run name>/<test file name>/<test name>/<role>_<idx>.log`
- Codex chronicles output is logged to stdout, which also written to file (see above)
2025-03-06 15:49:08 +11:00
if manager . config . showContinuousStatusUpdates :
2025-01-22 19:38:30 +11:00
let fut = manager . continuallyShowUpdates ( )
manager . trackedFutures . track fut
asyncSpawn fut
2025-01-16 16:14:25 +11:00
2025-02-28 15:25:41 +11:00
let futRunTests = manager . runTests ( )
manager . trackedFutures . track futRunTests
await futRunTests
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
2025-03-04 13:11:38 +11:00
proc stop * ( manager : TestManager ) {. async : ( raises : [ ] ) . } =
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 :
2025-03-04 13:11:38 +11:00
if not test . process . isNil :
2025-02-28 22:42:01 +11:00
try :
2025-03-04 13:11:38 +11:00
if test . process . running | ? false :
discard await noCancel test . process . terminateAndWaitForExit ( 100 . millis )
trace " Terminated running test process " , name = test . config . name
2025-02-28 22:42:01 +11:00
except AsyncProcessError , AsyncProcessTimeoutError :
warn " Test process failed to terminate, ignoring... " , name = test . config . name
finally :
2025-03-04 13:11:38 +11:00
trace " Closing test process ' streams " , name = test . config . name
await noCancel test . process . closeWait ( )
2025-02-28 22:42:01 +11:00
2024-12-20 16:23:40 +11:00
for hardhat in manager . hardhats :
try :
2025-01-29 15:55:17 +11:00
if not hardhat . process . isNil :
2025-02-28 22:42:01 +11:00
await noCancel hardhat . process . stop ( )
2025-03-04 13:11:38 +11:00
trace " Terminated running hardhat process "
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