## Unit-tests for the CDDL schema generator. Drives it directly against a ## synthetic `ffiProcRegistry` / `ffiTypeRegistry` so we don't need to invoke ## the macro pipeline (and thus don't write any files). import std/strutils import unittest2 import ../ffi/codegen/[meta, cddl] proc fieldsOf(pairs: openArray[(string, string)]): seq[FFIFieldMeta] = var res = @[] for p in pairs: res.add(FFIFieldMeta(name: p[0], typeName: p[1])) return res proc paramsOf(triples: openArray[(string, string, bool)]): seq[FFIParamMeta] = var res = @[] for t in triples: res.add(FFIParamMeta(name: t[0], typeName: t[1], isPtr: t[2])) return res proc field(n, t: string): FFIFieldMeta = FFIFieldMeta(name: n, typeName: t) proc param(n, t: string, isPtr = false): FFIParamMeta = FFIParamMeta(name: n, typeName: t, isPtr: isPtr) suite "nimTypeToCddl primitive mapping": test "primitives map to CDDL builtins": check nimTypeToCddl("bool") == "bool" check nimTypeToCddl("int") == "int" check nimTypeToCddl("int32") == "int" check nimTypeToCddl("uint64") == "uint" check nimTypeToCddl("float64") == "float64" check nimTypeToCddl("string") == "tstr" check nimTypeToCddl("cstring") == "tstr" check nimTypeToCddl("pointer") == "uint" test "pointer types map to uint": check nimTypeToCddl("ptr Foo") == "uint" test "seq[T] becomes [* T]": check nimTypeToCddl("seq[int]") == "[* int]" check nimTypeToCddl("seq[string]") == "[* tstr]" test "Option[T] becomes T / nil": check nimTypeToCddl("Option[int]") == "int / nil" check nimTypeToCddl("Maybe[string]") == "tstr / nil" test "nested generics": check nimTypeToCddl("seq[Option[int]]") == "[* int / nil]" test "unknown PascalCase is passed through as a rule reference": check nimTypeToCddl("EchoRequest") == "EchoRequest" suite "generateCddlSchema": setup: let types = @[ FFITypeMeta( name: "EchoRequest", fields: @[field("message", "string"), field("delayMs", "int")], ), FFITypeMeta(name: "EchoResponse", fields: @[field("echoed", "string")]), ] let procs = @[ FFIProcMeta( procName: "nimtimer_create", libName: "nimtimer", kind: ffiCtorKind, libTypeName: "NimTimer", extraParams: @[param("config", "TimerConfig")], returnTypeName: "NimTimer", returnIsPtr: false, isAsync: true, ), FFIProcMeta( procName: "nimtimer_echo", libName: "nimtimer", kind: ffiFfiKind, libTypeName: "NimTimer", extraParams: @[param("req", "EchoRequest")], returnTypeName: "EchoResponse", returnIsPtr: false, isAsync: true, ), FFIProcMeta( procName: "nimtimer_destroy", libName: "nimtimer", kind: ffiDtorKind, libTypeName: "NimTimer", extraParams: @[], returnTypeName: "", returnIsPtr: false, isAsync: false, ), ] let cddl = generateCddlSchema(procs, types, "nimtimer", "../nim_timer.nim") test "header references the source file": check "../nim_timer.nim" in cddl check "CBOR (RFC 8949)" in cddl test "user-declared types become CDDL map rules": check "EchoRequest = { message: tstr, delayMs: int }" in cddl check "EchoResponse = { echoed: tstr }" in cddl test "per-proc request envelopes are emitted": check "NimtimerCreateCtorReq = { config: TimerConfig }" in cddl check "NimtimerEchoReq = { req: EchoRequest }" in cddl test "proc request/response rules": check "nimtimer_create-request = NimtimerCreateCtorReq" in cddl check "nimtimer_create-response = tstr" in cddl check "nimtimer_echo-request = NimtimerEchoReq" in cddl check "nimtimer_echo-response = EchoResponse" in cddl test "dtor has no request envelope and a nil response": check "nimtimer_destroy-request" notin cddl check "nimtimer_destroy-response = nil" in cddl test "kind tags appear in proc comments": check "; nimtimer_create (ctor)" in cddl check "; nimtimer_echo (async)" in cddl check "; nimtimer_destroy (dtor)" in cddl