From a12dc6ea64af3df12e8c1a0e60901d81bc91b52a Mon Sep 17 00:00:00 2001 From: Eric <5089238+emizzle@users.noreply.github.com> Date: Wed, 5 Nov 2025 18:05:35 +1100 Subject: [PATCH] remove testmanager Not needed for now since parallel integration tests aren't enabled --- tests/integration/testmanager.nim | 796 ------------------------------ 1 file changed, 796 deletions(-) delete mode 100644 tests/integration/testmanager.nim diff --git a/tests/integration/testmanager.nim b/tests/integration/testmanager.nim deleted file mode 100644 index 145ce40e..00000000 --- a/tests/integration/testmanager.nim +++ /dev/null @@ -1,796 +0,0 @@ -import std/os -import std/strformat -import std/terminal -from std/times import fromUnix, format, now -from std/unicode import toUpper -import pkg/chronos -import pkg/chronos/asyncproc -import pkg/codex/conf -import pkg/codex/logutils -import pkg/codex/utils/trackedfutures -import pkg/questionable -import pkg/questionable/results -import ./hardhatprocess -import ./utils -import ../examples - -type - Hardhat = ref object - process: HardhatProcess - port: int - - TestManagerConfig* = object # Echoes stdout from Hardhat process - debugHardhat*: bool - # Shows all log topics at TRACE log level by disabling codex node output log - # topic filters, eg libp2p, websock, JSON RPC - noCodexLogFilters*: bool - # 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 - - TestManager* = ref object - config: TestManagerConfig - testConfigs: seq[IntegrationTestConfig] - tests: seq[IntegrationTest] - hardhats: seq[Hardhat] - lastHardhatPort: int - lastCodexApiPort: int - lastCodexDiscPort: int - timeStart: ?Moment - timeEnd: ?Moment - codexPortLock: AsyncLock - hardhatPortLock: AsyncLock - hardhatProcessLock: AsyncLock - trackedFutures: TrackedFutures - - IntegrationTestConfig* = object - startHardhat: bool - testFile: string - name: string - - IntegrationTestStatus = enum ## The status of a test when it is done. - 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). - - IntegrationTest = ref object - manager: TestManager - config: IntegrationTestConfig - process: AsyncProcessRef - timeStart: ?Moment - timeEnd: ?Moment - output: ?!TestOutput - testId: string # when used in datadir path, prevents data dir clashes - status: IntegrationTestStatus - command: string - logsDir: string - - TestOutput = ref object - stdOut*: seq[string] - stdErr*: seq[string] - exitCode*: ?int - - TestManagerError* = object of CatchableError - - Border {.pure.} = enum - Left - Right - - Align {.pure.} = enum - Left - Right - - MarkerPosition {.pure.} = enum - Start - Finish - -{.push raises: [].} - -logScope: - topics = "testing integration testmanager" - -proc printOutputMarker( - test: IntegrationTest, position: MarkerPosition, msg: string -) {.gcsafe, raises: [].} - -proc raiseTestManagerError( - msg: string, parent: ref CatchableError = nil -) {.raises: [TestManagerError].} = - raise newException(TestManagerError, msg, parent) - -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 - -template ignoreCancelled(body) = - try: - body - except CancelledError: - discard - -func logFile*(_: type TestManager, dir: string): string = - dir / "testmanager.chronicles.log" - -func logFile(manager: TestManager): string = - TestManager.logFile(manager.config.logsDir) - -func logFile(test: IntegrationTest, fileName: string): string = - 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 - o =? output and o.stdOut.len < 3 - -proc new*( - _: type TestManager, - config: TestManagerConfig, - testConfigs: seq[IntegrationTestConfig], -): TestManager = - TestManager( - config: config, - testConfigs: testConfigs, - lastHardhatPort: 8545, - lastCodexApiPort: 8000, - lastCodexDiscPort: 18000, # keep separated by 10000 to minimise overlap - trackedFutures: TrackedFutures.new(), - ) - -func init*( - _: type IntegrationTestConfig, testFile: string, startHardhat: bool, name = "" -): IntegrationTestConfig = - IntegrationTestConfig( - testFile: testFile, - name: if name == "": testFile.extractFilename else: name, - startHardhat: startHardhat, - ) - -template withLock*(lock: AsyncLock, body: untyped) = - if lock.isNil: - lock = newAsyncLock() - - await lock.acquire() - try: - body - finally: - try: - lock.release() - except AsyncLockError as parent: - raiseTestManagerError "lock error", parent - -proc duration(manager: TestManager): Duration = - let now = Moment.now() - (manager.timeEnd |? now) - (manager.timeStart |? now) - -proc allTestsPassed*(manager: TestManager): ?!bool = - for test in manager.tests: - if test.status in {IntegrationTestStatus.New, IntegrationTestStatus.Running}: - return failure "Integration tests not complete" - - if test.status != IntegrationTestStatus.Ok: - return success false - - return success true - -proc duration(test: IntegrationTest): Duration = - let now = Moment.now() - (test.timeEnd |? now) - (test.timeStart |? now) - -proc startHardhat( - test: IntegrationTest -): Future[Hardhat] {.async: (raises: [CancelledError, TestManagerError]).} = - var args: seq[string] = @[] - var port: int - - let hardhat = Hardhat.new() - - withLock(test.manager.hardhatPortLock): - port = await nextFreePort(test.manager.lastHardhatPort + 1) - test.manager.lastHardhatPort = port - - args.add("--port") - args.add($port) - if test.manager.config.debugHardhat: - args.add("--log-file=" & test.logsDir / "hardhat.log") - - trace "starting hardhat process on port ", port - try: - withLock(test.manager.hardhatProcessLock): - let node = await HardhatProcess.startNode( - args, false, "hardhat for '" & test.config.name & "'" - ) - hardhat.process = node - hardhat.port = port - await node.waitUntilStarted() - return hardhat - except CancelledError as e: - raise e - except CatchableError as e: - raiseTestManagerError "hardhat node failed to start: " & e.msg, e - -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) = - if position == MarkerPosition.Start: - echo "" - - echoStyled styleBright, - bgWhite, fgBlack, &"----- {toUpper $position} {test.config.name} {msg} -----" - - if position == MarkerPosition.Finish: - echo "" - -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)) - -proc printResult(test: IntegrationTest, printStdOut, printStdErr: bool) = - case test.status - 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") - if output =? test.output: - if printStdErr: - test.printOutputMarker(MarkerPosition.Start, "test file errors (stderr)") - echo output.stdErr.join("\n") - test.printOutputMarker(MarkerPosition.Finish, "test file errors (stderr)") - of IntegrationTestStatus.Failed: - if output =? test.output: - if printStdErr: - test.printOutputMarker(MarkerPosition.Start, "test file errors (stderr)") - echo output.stdErr.join("\n") - test.printOutputMarker(MarkerPosition.Finish, "test file errors (stderr)") - if printStdOut: - test.printOutputMarker(MarkerPosition.Start, "codex node output (stdout)") - echo output.stdOut.join("\n").colorise - test.printOutputMarker(MarkerPosition.Finish, "codex node output (stdout)") - test.printResult(fgRed) - of IntegrationTestStatus.Timeout: - if printStdOut and output =? test.output: - test.printOutputMarker(MarkerPosition.Start, "codex node output (stdout)") - echo output.stdOut.join("\n").colorise - test.printOutputMarker(MarkerPosition.Finish, "codex node output (stdout)") - test.printResult(fgYellow) - of IntegrationTestStatus.Ok: - if printStdOut and output =? test.output: - test.printOutputMarker(MarkerPosition.Start, "codex node output (stdout)") - echo output.stdOut.join("\n").colorise - test.printOutputMarker(MarkerPosition.Finish, "codex node output (stdout)") - test.printResult(fgGreen) - -proc printSummary(test: IntegrationTest) = - test.printResult(printStdOut = false, printStdErr = false) - -proc printStart(test: IntegrationTest) = - echoStyled styleBright, - fgMagenta, &"[Integration test started] ", resetStyle, test.config.name - -proc buildCommand( - test: IntegrationTest, hardhatPort: ?int -): Future[string] {.async: (raises: [CancelledError, TestManagerError]).} = - var hhPort = string.none - if test.config.startHardhat: - without port =? hardhatPort: - raiseTestManagerError "hardhatPort required when 'config.startHardhat' is true" - hhPort = some "-d:HardhatPort=" & $port - - var testFile: string - try: - testFile = absolutePath( - test.config.testFile, root = currentSourcePath().parentDir().parentDir() - ) - except ValueError as parent: - raiseTestManagerError "bad file name, testFile: " & test.config.testFile, parent - - withLock(test.manager.codexPortLock): - # Increase the port by 1000 to allow each test to run 1000 codex nodes - # (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. Windows, in particular, does not - # like giving up ports. - let apiPort = await nextFreePort(test.manager.lastCodexApiPort + 1000) - test.manager.lastCodexApiPort = apiPort - let discPort = await nextFreePort(test.manager.lastCodexDiscPort + 1000) - test.manager.lastCodexDiscPort = discPort - - let codexLogLevel = - if test.manager.config.noCodexLogFilters: - "TRACE" - else: - "TRACE;disabled:libp2p,websock,JSONRPC-HTTP-CLIENT,JSONRPC-WS-CLIENT,discv5" - - withLock(test.manager.hardhatPortLock): - try: - return - #!fmt: off - "nim c " & - &"-d:CodexApiPort={apiPort} " & - &"-d:CodexDiscPort={discPort} " & - &"-d:CodexLogsDir={test.logsDir} " & - &"-d:CodexLogLevel=\"{codexLogLevel}\" " & - &"-d:CodexLogToFile=true " & - (hhPort |? "") & " " & - &"-d:TestId={test.testId} " & - # Log multinodes chronicles logs settings (log to file with no - # colours, and loglevel = TRACE). - "-d:chronicles_log_level=TRACE " & - "-d:chronicles_sinks=textlines[nocolors,file] " & - "-d:nimUnittestOutputLevel:VERBOSE " & - "--verbosity:0 " & - "--hints:off " & - "-d:release " & - "-r " & - &"{testFile}" - #!fmt: on - except ValueError as parent: - raiseTestManagerError "bad command --\n" & ", apiPort: " & $apiPort & - ", discPort: " & $discPort & ", testFile: " & testFile & ", error: " & - parent.msg, parent - -proc setup( - test: IntegrationTest -): Future[?Hardhat] {.async: (raises: [CancelledError, TestManagerError]).} = - var hardhat = Hardhat.none - var hardhatPort = int.none - - if test.config.startHardhat: - let hh = await test.startHardhat() - hardhat = some hh - hardhatPort = some hh.port - test.manager.hardhats.add hh - - test.command = await test.buildCommand(hardhatPort) - - return hardhat - -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) - -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: - let cmdResult = await forceKillProcess("nim.exe", &"-d:TestId {test.testId}") - if cmdResult.status > 0: - error "Failed to forcefully kill windows test process", - testId = test.testId, exitCode = cmdResult.status, stderr = cmdResult.stdError - else: - trace "Successfully killed windows test process by force", - testId = test.testId, - exitCode = cmdResult.status, - stdout = cmdResult.stdOutput - 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: - error "Failed to forcefully kill windows test process", - testId = test.testId, error = e.msg - except AsyncProcessTimeoutError as e: - error "Timeout while forcefully killing windows test process", - testId = test.testId, error = e.msg - -proc teardownTest(test: IntegrationTest) {.async: (raises: []).} = - logScope: - test = test.config.name - - trace "Tearing down test" - - test.timeEnd = some Moment.now() - - if not test.process.isNil: - var output = test.output.expect("should have output value") - if test.process.running |? false: - trace "Test process still running, terminating..." - try: - output.exitCode = - some (await noCancel test.process.terminateAndWaitForExit(1.seconds)) - trace "Test process terminated", exitCode = output.exitCode - except AsyncProcessError, AsyncProcessTimeoutError: - let e = getCurrentException() - warn "Test process failed to terminate, check for zombies", error = e.msg - - await test.closeProcessStreams() - test.process = nil - -proc teardown(test: IntegrationTest, hardhat: ?Hardhat) {.async: (raises: []).} = - if test.config.startHardhat and hardhat =? hardhat and not hardhat.process.isNil: - await test.teardownHardhat(hardhat) - - await test.teardownTest() - -proc untilTimeout( - 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. - - let timer = sleepAsync(timeout) - defer: - # 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. - await fut.cancelAndWait() - await timer.cancelAndWait() - - try: - discard await race(fut, timer) - except ValueError as e: - 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") - -proc captureOutput( - process: AsyncProcessRef, stream: AsyncStreamReader, filePath: string -): 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 != ""): - try: - output.add line - filePath.appendFile(line & "\n".stripAnsi) - await sleepAsync(1.nanos) - except IOError as e: - warn "Failed to write test stdout and/or stderr to file", error = e.msg - 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" - - let futStdOut = - test.process.captureOutput(test.process.stdoutStream, test.logFile("stdout.log")) - let futStdErr = - test.process.captureOutput(test.process.stderrStream, test.logFile("stderr.log")) - await allFutures(futStdOut, futStdErr) - return (await futStdOut, await futStdErr) - -proc start(test: IntegrationTest) {.async: (raises: []).} = - logScope: - name = test.config.name - duration = test.duration - - trace "Running test" - - 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 - - test.timeStart = some Moment.now() - test.status = IntegrationTestStatus.Running - - var hardhat = none Hardhat - - ignoreCancelled: - try: - hardhat = await test.setup() - except TestManagerError as e: - test.timeEnd = some Moment.now() - test.status = IntegrationTestStatus.Error - test.output = TestOutput.failure(e) - error "Failed to start hardhat and build command", error = e.msg - return - - trace "Starting parallel integration test", - command = test.command, timeout = test.manager.config.testTimeout - test.printStart() - - try: - 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 - - var futCaptureOutput: Future[(seq[string], seq[string])].Raising([CancelledError]) - 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` - await test.teardown(hardhat) # doesn't raise CancelledError, so noCancel not needed - await futCaptureOutput.cancelAndWait() - - var output = TestOutput.new() - test.output = success(output) - futCaptureOutput = test.captureProcessOutput() - - output.exitCode = - try: - some (await test.process.waitForExit(test.manager.config.testTimeout)) - except AsyncProcessTimeoutError as e: - test.timeEnd = some Moment.now() - test.status = IntegrationTestStatus.Timeout - error "Test process failed to exit before timeout", - timeout = test.manager.config.testTimeout - 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 - - let (stdOut, stdErr) = await futCaptureOutput - output.stdOut = stdOut - output.stdErr = stdErr - - test.status = - if output.exitCode == some QuitSuccess: - IntegrationTestStatus.Ok - elif output.exitCode == some QuitFailure: - IntegrationTestStatus.Failed - else: - IntegrationTestStatus.Error - -proc continuallyShowUpdates(manager: TestManager) {.async: (raises: []).} = - ignoreCancelled: - while true: - let sleepDuration = if manager.duration < 5.minutes: 30.seconds else: 1.minutes - - if manager.tests.len > 0: - echo "" - echoStyled styleBright, - bgWhite, fgBlack, &"Integration tests status after {manager.duration}" - - for test in manager.tests: - test.printSummary() - - if manager.tests.len > 0: - echo "" - - await sleepAsync(sleepDuration) - -proc run(test: IntegrationTest) {.async: (raises: []).} = - ignoreCancelled: - let futStart = test.start() - # await futStart - - try: - await futStart.untilTimeout(test.manager.config.testTimeout) - except AsyncTimeoutError: - test.timeEnd = some Moment.now() - test.status = IntegrationTestStatus.Timeout - # futStart will be cancelled by untilTimeout and that will run the - # teardown procedure (in defer) - - test.printResult( - printStdOut = test.status != IntegrationTestStatus.Ok, - printStdErr = - test.status == IntegrationTestStatus.Error or - (test.status == IntegrationTestStatus.Failed and test.output.isErrorLike), - ) - logScope: - name = test.config.name - duration = test.duration - - 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!" - - 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", - exitCode = test.output.option .? exitCode, - error = test.output.errorOption .? msg, - stack = test.output.errorOption .? getStackTrace() - of IntegrationTestStatus.Failed: - error "Test failed", exitCode = test.output.option .? exitCode - of IntegrationTestStatus.Timeout: - error "Test timed out" - of IntegrationTestStatus.Ok: - notice "Test passed" - -proc runTests(manager: TestManager) {.async: (raises: []).} = - var testFutures: seq[Future[void]] - - manager.timeStart = some Moment.now() - - echoStyled styleBright, - bgWhite, fgBlack, "\n[Integration Test Manager] Starting parallel integration tests" - notice "[Integration Test Manager] Starting parallel integration tests", - config = manager.config - - for config in manager.testConfigs: - var test = - IntegrationTest(manager: manager, config: config, testId: $uint16.example) - manager.tests.add test - - let futRun = test.run() - testFutures.add futRun - - try: - await allFutures testFutures - manager.timeEnd = some Moment.now() - except CancelledError as e: - discard - finally: - for fut in testFutures: - await fut.cancelAndWait() - -proc withBorder( - msg: string, align = Align.Left, width = 67, borders = {Border.Left, Border.Right} -): string = - if borders.contains(Border.Left): - result &= "| " - if align == Align.Left: - result &= msg.alignLeft(width) - elif align == Align.Right: - result &= msg.align(width) - if borders.contains(Border.Right): - result &= " |" - -proc printResult(manager: TestManager) = - var successes = 0 - var totalDurationSerial: Duration - echo "" - echoStyled styleBright, styleUnderscore, bgWhite, fgBlack, &"INTEGRATION TESTS RESULT" - - for test in manager.tests: - totalDurationSerial += test.duration - if test.status == IntegrationTestStatus.Ok: - inc successes - # because debug output can really make things hard to read, show a nice - # summary of test results - test.printSummary() - - # estimated time saved as serial execution with a single hardhat instance - # incurs less overhead - let relativeTimeSaved = - ((totalDurationSerial - manager.duration).nanos * 100) div - (totalDurationSerial.nanos) - let passingStyle = if successes < manager.tests.len: fgRed else: fgGreen - - echo "\n▢=====================================================================▢" - echoStyled "| ", - styleBright, - styleUnderscore, - "INTEGRATION TEST SUMMARY", - resetStyle, - "".withBorder(Align.Right, 43, {Border.Right}) - echo "".withBorder() - 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, - " |" - echo "▢=====================================================================▢" - notice "INTEGRATION TEST SUMMARY", - totalTime = manager.duration, - timeSavedEst = &"{relativeTimeSaved}%", - passing = &"{successes} / {manager.tests.len}" - -proc start*(manager: TestManager) {.async: (raises: [CancelledError]).} = - if manager.config.showContinuousStatusUpdates: - let fut = manager.continuallyShowUpdates() - manager.trackedFutures.track fut - asyncSpawn fut - - let futRunTests = manager.runTests() - manager.trackedFutures.track futRunTests - - await futRunTests - - manager.printResult() - -proc stop*(manager: TestManager) {.async: (raises: []).} = - await manager.trackedFutures.cancelTracked() - - for test in manager.tests: - if not test.process.isNil: - try: - if test.process.running |? false: - discard await noCancel test.process.terminateAndWaitForExit(100.millis) - trace "Terminated running test process", name = test.config.name - except AsyncProcessError, AsyncProcessTimeoutError: - warn "Test process failed to terminate, ignoring...", name = test.config.name - finally: - trace "Closing test process' streams", name = test.config.name - await noCancel test.process.closeWait() - - for hardhat in manager.hardhats: - try: - if not hardhat.process.isNil: - await noCancel hardhat.process.stop() - trace "Terminated running hardhat process" - except CatchableError as e: - trace "failed to stop hardhat node", error = e.msg