/* * test_common.h -- shared CHECK macros for the test binaries. * * Each test .c keeps its own file-local storage: * static int fail_count = 0; * static const char *current_test = ""; * * CHECK_OK jumps to a label named `out` in the caller. */ #ifndef CLICKHOUSE_TEST_COMMON_H #define CLICKHOUSE_TEST_COMMON_H #include #include #include #include #include #define CHECK(cond) do { \ if (!(cond)) { \ fprintf(stderr, "%s:%d %s: FAIL: %s\n", \ __FILE__, __LINE__, current_test, #cond); \ fail_count++; \ } \ } while (0) #define CHECK_EQ_U64(actual, expected) do { \ uint64_t a = (uint64_t) (actual), b = (uint64_t) (expected); \ if (a != b) { \ fprintf(stderr, "%s:%d %s: FAIL: %s == %" PRIu64 \ " (got %" PRIu64 ")\n", \ __FILE__, __LINE__, current_test, #actual, b, a); \ fail_count++; \ } \ } while (0) #define CHECK_EQ_I64(actual, expected) do { \ int64_t a = (int64_t) (actual), b = (int64_t) (expected); \ if (a != b) { \ fprintf(stderr, "%s:%d %s: FAIL: %s == %" PRId64 \ " (got %" PRId64 ")\n", \ __FILE__, __LINE__, current_test, #actual, b, a); \ fail_count++; \ } \ } while (0) #define CHECK_STR_EQ(actual, alen, expected) do { \ size_t elen = strlen(expected); \ if ((alen) != elen || memcmp((actual), (expected), elen) != 0) {\ fprintf(stderr, "%s:%d %s: FAIL: \"%.*s\" != \"%s\"\n", \ __FILE__, __LINE__, current_test, \ (int)(alen), (actual), (expected)); \ fail_count++; \ } \ } while (0) /* CHECK_OK: jumps to `out:` on failure. Pair with chc_err err = {}; * and an out: label that frees / closes / returns. */ #define CHECK_OK(rc, err) do { \ if ((rc) != CHC_OK) { \ fprintf(stderr, "%s:%d %s: FAIL: rc=%d err='%s'\n", \ __FILE__, __LINE__, current_test, (rc), (err).msg); \ fail_count++; goto out; \ } \ } while (0) typedef struct test_mem_src { const uint8_t *data; size_t len; size_t pos; } test_mem_src; typedef struct test_mem_sink { uint8_t *data; size_t len; size_t cap; } test_mem_sink; CHC_MAYBE_UNUSED static int test_mem_err_set(chc_err *err, int code, const char *msg) { if (err) { err->server_code = 0; snprintf(err->msg, sizeof err->msg, "%s", msg); err->server_name[0] = '\0'; } return code; } CHC_MAYBE_UNUSED static int test_mem_read(void *ud, void *buf, size_t len, size_t *out_n, chc_err *err) { (void) err; test_mem_src *m = ud; size_t avail = m->len - m->pos; size_t take = len < avail ? len : avail; if (take) memcpy(buf, m->data + m->pos, take); m->pos += take; *out_n = take; return CHC_OK; } CHC_MAYBE_UNUSED static void test_mem_src_init(test_mem_src *src, chc_io *io, const void *data, size_t len) { *src = (test_mem_src) { .data = data, .len = len }; *io = (chc_io) { .ud = src, .read = test_mem_read }; } /* Read one block via a throwaway chc_in over io -- for single-block sources. * Streaming callers own a persistent chc_in and call chc_block_read directly. */ CHC_MAYBE_UNUSED static int test_block_read_io(chc_io *io, const chc_alloc *al, const chc_block_opts *opts, chc_block **out, chc_err *err) { chc_in in; int rc = chc_in_init(&in, io, al, opts->read_buffer_bytes, err); if (rc != CHC_OK) return rc; rc = chc_block_read(&in, al, opts, out, err); chc_in_free(&in); return rc; } CHC_MAYBE_UNUSED static int test_mem_sink_write(void *ud, const void *buf, size_t n, chc_err *err) { test_mem_sink *s = ud; if (n > SIZE_MAX - s->len) return test_mem_err_set(err, CHC_ERR_OOM, "mem sink size overflow"); size_t need = s->len + n; if (need > s->cap) { size_t nc = s->cap ? s->cap : 256; while (nc < need) { if (nc > SIZE_MAX / 2) { nc = need; break; } nc *= 2; } uint8_t *nb = realloc(s->data, nc); if (!nb) return test_mem_err_set(err, CHC_ERR_OOM, "mem sink oom"); s->data = nb; s->cap = nc; } if (n) memcpy(s->data + s->len, buf, n); s->len += n; return CHC_OK; } CHC_MAYBE_UNUSED static void test_mem_sink_init(test_mem_sink *sink, chc_io *io) { *sink = (test_mem_sink) {}; *io = (chc_io) { .ud = sink, .write = test_mem_sink_write }; } CHC_MAYBE_UNUSED static void test_mem_sink_free(test_mem_sink *sink) { free(sink->data); *sink = (test_mem_sink) {}; } #endif