Files
nim-namespaced-logging/tests/regressions.nim
T

91 lines
3.2 KiB
Nim

import std/[os, osproc, strutils, tempfiles, unittest]
import ../src/namespaced_logging
type
CloneControl = object
failNext: bool
FallibleAppender = ref object of LogAppender
control: ptr CloneControl
method clone(app: FallibleAppender): LogAppender {.gcsafe.} =
if app.control.failNext:
app.control.failNext = false
raise newException(ValueError, "injected clone failure")
FallibleAppender(control: app.control)
type
LifetimeProbeObj = object
destroyed: ptr int
LifetimeProbe = ref LifetimeProbeObj
proc `=destroy`(probe: LifetimeProbeObj) =
inc probe.destroyed[]
proc ignoreMessage(state: LifetimeProbe, msg: LogMessage) {.gcsafe, noconv.} =
discard
proc createAndReleaseService(destroyed: ptr int) =
var service = initLogService()
service.addAppender(initCustomLogAppender(
LifetimeProbe(destroyed: destroyed), ignoreMessage))
# All owning handles leave scope here. No thread or cycle is involved.
suite "ORC ownership and configuration regressions":
test "isEnabled observes a namespace change before any log call":
var service = initLogService()
service.setThreshold("worker", lvlError)
let local = service.threadLocalRef()
let logger = local.getLogger("worker")
check not logger.isEnabled(lvlInfo)
# Simulate a change from another service handle without introducing a
# reference-count race into this deterministic configuration test.
service.setThreshold("worker", lvlInfo)
check logger.isEnabled(lvlInfo)
test "a failed refresh is retried without another configuration change":
var control: CloneControl
var service = initLogService()
service.addAppender(FallibleAppender(control: addr control))
let local = service.threadLocalRef()
discard local.getLogger("worker")
service.setRootThreshold(lvlInfo)
control.failNext = true
expect ValueError:
local.ensureFreshness()
# clone now succeeds; the failed refresh must not have marked the
# incomplete local configuration as current.
local.ensureFreshness()
check local.appenders().len == 1
test "releasing the service releases state owned by its appenders":
var destroyed = 0
createAndReleaseService(addr destroyed)
check destroyed == 1
suite "Background writer shutdown regressions":
for destination in ["file", "console"]:
test destination & " writer drains accepted messages at normal process exit":
let directory = createTempDir("namespaced_logging_shutdown_", "")
let outputPath = directory / "messages.log"
defer: removeDir(directory)
let child = startProcess(parentDir(getAppFilename()) / "writer_child",
args = [destination, outputPath], options = {poParentStreams})
defer: close(child)
let exitCode = child.waitForExit(15_000)
if exitCode == -1:
child.kill()
discard child.waitForExit()
check exitCode == 0
require fileExists(outputPath)
let received = readFile(outputPath).splitLines()
# Check every record and its order, not just file existence. splitLines
# includes one final empty item because each record ends with a newline.
check received.len == 10_001
for i in 0 ..< min(10_000, received.len - 1):
check received[i] == "message " & $i