nim-ffi/tests/unit/test_ctx_validation.nim

58 lines
1.7 KiB
Nim
Raw Permalink Normal View History

import std/[atomics, locks]
2026-05-11 19:21:40 -03:00
import unittest2
import results
2026-05-19 12:43:34 +02:00
import ffi
2026-05-11 19:21:40 -03:00
type TestLib = object
type CtxValidationConfig {.ffi.} = object
initialValue: int
proc ctxval_create*(
config: CtxValidationConfig
): Future[Result[TestLib, string]] {.ffiCtor.} =
return ok(TestLib())
proc ctxval_ping*(lib: TestLib): Future[Result[string, string]] {.ffi.} =
return ok("pong")
type CallbackState = object
lock: Lock
called: Atomic[bool]
retCode: cint
proc initCbState(s: var CallbackState) =
s.lock.initLock()
s.called.store(false)
proc validationCallback(
2026-05-11 19:21:40 -03:00
retCode: cint, msg: ptr cchar, len: csize_t, userData: pointer
) {.cdecl, gcsafe, raises: [].} =
let s = cast[ptr CallbackState](userData)
s[].retCode = retCode
s[].called.store(true)
suite "ctx pointer validation at the FFI entry point":
# The macro-generated FFI entry point validates ctx via
# <LibType>FFIPool.isValidCtx. Any caller — C or Nim — that passes a nil or
# offset-invalid ctx with a valid callback should receive RET_ERR via the
# callback and the proc should return RET_ERR, never crash.
test "nil ctx with valid callback returns RET_ERR via callback, no crash":
var s: CallbackState
initCbState(s)
2026-05-11 19:21:40 -03:00
let nilCtx: ptr FFIContext[TestLib] = nil
let ret = ctxval_pingCborExport(nilCtx, validationCallback, addr s, nil, 0.csize_t)
check ret == RET_ERR
check s.called.load()
check s.retCode == RET_ERR
test "invalid non-nil ctx (offset-pointer) returns RET_ERR, no crash":
var s: CallbackState
initCbState(s)
2026-05-11 19:21:40 -03:00
let invalidCtx = cast[ptr FFIContext[TestLib]](123)
let ret = ctxval_pingCborExport(invalidCtx, validationCallback, addr s, nil, 0.csize_t)
check ret == RET_ERR
check s.called.load()
check s.retCode == RET_ERR