japl/tests/logutils.nim

155 lines
5.2 KiB
Nim
Raw Permalink Normal View History

2021-01-30 17:31:54 +01:00
# Copyright 2020 Mattia Giambirtone
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
## A quick library for writing debug logs, errors, fatals and progress bars
## for the test suite.
##
## Global variables:
##
## totalLog (can be written to with the proc log)
## verbose (can be set with the proc setVerbosity)
## logfiles (can be set with the proc setLogfiles)
##
## The rationale behind all three is that they have one value accross
## one jats process/instance, and they would bloat up every single proc
## signature, because they are needed for the proc log to work.
2021-01-30 17:00:42 +01:00
2021-01-31 15:34:41 +01:00
import terminal
import strformat
import times
import strutils
2021-01-30 17:00:42 +01:00
type LogLevel* {.pure.} = enum
## All the different possible log levels
2021-01-30 17:00:42 +01:00
Debug, # always written to file only (large outputs, such as the entire output of the failing test or stacktrace)
Info, # important information about the progress of the test suite
Enumeration, # a white output for the enumerate option
Error, # failing tests (printed with yellow)
Fatal # always printed with red, halts the entire suite (test parsing errors, printed with red)
2021-01-30 17:00:42 +01:00
# log config: which log levels to show, show in silent mode and save to the
# detailed debug logs
const echoedLogs = {LogLevel.Info, LogLevel.Error, LogLevel.Fatal,
LogLevel.Enumeration}
const echoedLogsSilent = {LogLevel.Fatal, LogLevel.Enumeration} # will be echoed even if test suite is silent
const savedLogs = {LogLevel.Debug, LogLevel.Info, LogLevel.Error,
LogLevel.Fatal, LogLevel.Enumeration}
2021-01-30 17:00:42 +01:00
# aesthetic config:
# progress bar length
const progbarLength = 25
# log level colors
const logColors = [LogLevel.Debug: fgDefault, LogLevel.Info: fgGreen,
LogLevel.Enumeration: fgDefault,
LogLevel.Error: fgYellow, LogLevel.Fatal: fgRed]
2021-01-30 17:00:42 +01:00
# global vars for the proc log
2021-01-30 17:00:42 +01:00
var totalLog = ""
var verbose = true
var logfiles: seq[string]
2021-01-31 15:34:41 +01:00
# simple interfaces with the globals
2021-01-30 17:00:42 +01:00
proc setVerbosity*(verb: bool) =
## Sets the logging verbosity
2021-01-30 17:00:42 +01:00
verbose = verb
proc getTotalLog*: string =
## Returns all the detailed logs in ever logged in the jats instance
totalLog
proc setLogfiles*(files: seq[string]) =
## Sets files to write logs to
logfiles = files
# main logging command
2021-01-30 17:00:42 +01:00
proc log*(level: LogLevel, msg: string) =
## Adds a line to the total logs/stdout depending on config, together
## with the timestamp
2021-01-30 17:00:42 +01:00
let msg = &"[{$level} - {$getTime()}] {msg}"
if level in savedLogs:
totalLog &= msg & "\n"
if logfiles.len() > 0:
for file in logfiles:
let handle = file.open(fmAppend)
handle.writeLine(msg)
handle.close()
2021-01-30 17:00:42 +01:00
if (verbose and (level in echoedLogs)) or ((not verbose) and (level in echoedLogsSilent)):
setForegroundColor(logColors[level])
echo msg
setForegroundColor(fgDefault)
type FatalError* = ref object of CatchableError
proc fatal*(msg: string) =
## Creates a fatal error, logs it and raises it as an exception
log(LogLevel.Fatal, msg)
let e = new(FatalError)
e.msg = msg
raise e
# progress bar stuff
2021-01-30 17:00:42 +01:00
type Buffer* = ref object
## Represents an updateable line on the terminal
2021-01-30 17:00:42 +01:00
contents: string
previous: string
termwidth: int
2021-01-30 17:00:42 +01:00
proc newBuffer*: Buffer =
## Creates a Buffer, hides the cursor
hideCursor()
2021-01-30 17:00:42 +01:00
new(result)
proc updateProgressBar*(buf: Buffer, text: string, total: int, current: int) =
## Fills a buffer with a progress bar with label (text) total cells (total)
## and filled cells (current)
if total <= 0:
return
2021-01-30 17:00:42 +01:00
var newline = ""
newline &= "["
let ratio = current / total
let filledCount = int(ratio * progbarLength)
2021-01-30 17:26:40 +01:00
if filledCount > 0:
newline &= "=".repeat(filledCount)
if progbarLength - filledCount - 1 > 0:
2021-01-30 17:26:40 +01:00
newline &= " ".repeat(progbarLength - filledCount - 1)
2021-01-30 17:00:42 +01:00
newline &= &"] ({current}/{total}) {text}"
# to avoid process switching during half-written progress bars and whatnot all terminal editing happens at the end
2021-01-30 17:26:40 +01:00
let w = terminalWidth()
if w > newline.len():
newline &= " ".repeat(w - newline.len() - 1)
else:
newline = newline[0..w-2]
2021-01-30 17:00:42 +01:00
buf.contents = newline
2021-01-30 17:26:40 +01:00
proc clearLineAndWrite(text: string, oldsize: int) =
## writes text to the beginning of the line
# oldsize is there for history, and so that the implementation
# of line clearing is flexible
write stdout, "\r" & text & "\r"
2021-01-30 17:26:40 +01:00
2021-01-30 17:00:42 +01:00
proc render*(buf: Buffer) =
if verbose: #and buf.previous != buf.contents:
2021-01-30 17:26:40 +01:00
clearLineAndWrite(buf.contents, buf.previous.len())
2021-01-30 17:00:42 +01:00
buf.previous = buf.contents
2021-01-30 17:26:40 +01:00
proc endBuffer*(buf: Buffer) =
## Ends the existence of a buffer
## restores terminal status for good scrolling experience
2021-01-30 17:26:40 +01:00
showCursor()