mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-07-17 21:39:28 +00:00
100 lines
3.2 KiB
C
100 lines
3.2 KiB
C
/* End-to-end test for the CBOR-free `abi = c` echo bindings. Unlike the CBOR C
|
|
* backend, this header links no TinyCBOR: the flat structs in echo.h are the C
|
|
* ABI, strings are plain borrowed `const char*`. The test drives the same
|
|
* async, callback-per-call surface — constructor, an object-returning method
|
|
* and teardown — copying out what each callback delivers (owned by the binding,
|
|
* valid only for the call) and polling a `done` flag to sequence the async
|
|
* calls. A string-returning method (echoVersion) rides the CBOR-free scalar
|
|
* fast path instead of a flat `_CWire` wrapper, so it has no c_abi binding yet
|
|
* (foreign codegen for the scalar shape is a follow-up) and isn't exercised
|
|
* here. */
|
|
#include "echo.h"
|
|
#include <assert.h>
|
|
#include <stdatomic.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
/* The `done` flag is a C11 atomic: the callback fires on the FFI thread and
|
|
* stores it with release ordering after filling the waiter's fields, and the
|
|
* poller loads it with acquire ordering — so the field writes are visible
|
|
* (and race-free under TSan) once `done` is seen set. */
|
|
static void wait_done(atomic_int* done) {
|
|
for (int i = 0; i < 500 && !atomic_load_explicit(done, memory_order_acquire); i++) {
|
|
struct timespec t = {0, 10 * 1000 * 1000}; /* 10ms */
|
|
nanosleep(&t, NULL);
|
|
}
|
|
assert(atomic_load_explicit(done, memory_order_acquire));
|
|
}
|
|
|
|
typedef struct {
|
|
atomic_int done;
|
|
int err_code;
|
|
EchoCtx* ctx;
|
|
char err[256];
|
|
} CreateWaiter;
|
|
|
|
static void on_created(int ec, EchoCtx* ctx, const char* em, void* ud) {
|
|
CreateWaiter* w = (CreateWaiter*)ud;
|
|
w->err_code = ec;
|
|
w->ctx = ctx;
|
|
if (em) {
|
|
snprintf(w->err, sizeof(w->err), "%s", em);
|
|
}
|
|
atomic_store_explicit(&w->done, 1, memory_order_release);
|
|
}
|
|
|
|
static EchoCtx* make_ctx(void) {
|
|
CreateWaiter w;
|
|
memset(&w, 0, sizeof(w));
|
|
EchoConfig config = {"c-abi"};
|
|
echo_ctx_create(&config, on_created, &w);
|
|
wait_done(&w.done);
|
|
if (w.err_code != 0) {
|
|
fprintf(stderr, "create failed: %s\n", w.err[0] ? w.err : "?");
|
|
}
|
|
assert(w.err_code == 0);
|
|
assert(w.ctx != NULL);
|
|
return w.ctx;
|
|
}
|
|
|
|
typedef struct {
|
|
atomic_int done;
|
|
int err_code;
|
|
char err[256];
|
|
char text_a[256];
|
|
char text_b[256];
|
|
} ReplyWaiter;
|
|
|
|
static void on_shout(int ec, const ShoutResponse* reply, const char* em, void* ud) {
|
|
ReplyWaiter* w = (ReplyWaiter*)ud;
|
|
w->err_code = ec;
|
|
if (reply) {
|
|
if (reply->shouted)
|
|
snprintf(w->text_a, sizeof(w->text_a), "%s", reply->shouted);
|
|
if (reply->prefix)
|
|
snprintf(w->text_b, sizeof(w->text_b), "%s", reply->prefix);
|
|
}
|
|
if (em) snprintf(w->err, sizeof(w->err), "%s", em);
|
|
atomic_store_explicit(&w->done, 1, memory_order_release);
|
|
}
|
|
|
|
static void test_shout(EchoCtx* ctx) {
|
|
ReplyWaiter w;
|
|
memset(&w, 0, sizeof(w));
|
|
ShoutRequest req = {"hello"};
|
|
echo_ctx_shout(ctx, &req, on_shout, &w);
|
|
wait_done(&w.done);
|
|
assert(w.err_code == 0);
|
|
assert(strcmp(w.text_a, "c-abi: HELLO") == 0);
|
|
assert(strcmp(w.text_b, "c-abi") == 0);
|
|
}
|
|
|
|
int main(void) {
|
|
EchoCtx* ctx = make_ctx();
|
|
test_shout(ctx);
|
|
echo_ctx_destroy(ctx);
|
|
printf("all abi=c echo e2e checks passed\n");
|
|
return 0;
|
|
}
|