Files

759 lines
23 KiB
C

/* LINK: ../../src/detect/probe.c ../../src/detect/checks.c ../../src/detect/check_registry.c ../../src/kdl/schema.c ../../src/kdl/parser.c ../../src/kdl/lexer.c ../../src/kdl/value.c ../../src/gen/sh_emit.c ../../src/error.c ../../src/span.c */
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L /* mkdtemp, system/WEXITSTATUS */
#endif
/*
* tests/unit/test_probe.c
*
* Unit tests for the configure-time probe snippet generator (todo 12):
* src/detect/probe.c. THE MODEL: probes do NOT run here, at stupidtools
* generation time — this module only EMITS POSIX-sh that runs at configure
* time inside the generated ./configure. These tests prove that emitted
* shell is syntactically valid (sh/bash/zsh -n), genuinely executes (real
* `cc` against real headers), short-circuits on config.cache, cross-compile
* guards RUN probes, and keeps hostile probe targets inert via st_sh_quote.
*
* The magic LINK comment on line 1 is REQUIRED by tests/run.sh (extra .c
* sources, relative to tests/unit/). probe.c needs sh_emit.c (st_sh_quote)
* + error.c + span.c; checks.c/check_registry.c/parser.c/lexer.c/value.c/
* schema.c are linked so canonical probes are built END-TO-END from DSL
* source (st_check_probe_build), exercising the real checks.c -> probe.c
* contract rather than hand-built structs alone.
*/
#include "munit.h"
#include "detect/check_registry.h"
#include "detect/checks.h"
#include "detect/probe.h"
#include "error.h"
#include "kdl/ast.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
/* ---- per-test temp dir ------------------------------------------------- */
static char temp_dir[128];
static void *
setup(const MunitParameter params[], void *user_data)
{
(void)params;
(void)user_data;
int n = snprintf(temp_dir, sizeof temp_dir, "/tmp/st_probe_XXXXXX");
if (n < 0) {
return NULL;
}
if (mkdtemp(temp_dir) == NULL) {
return NULL;
}
return (void *)1;
}
static void
teardown(void *fixture)
{
char cmd[160];
int n;
if (fixture == NULL || temp_dir[0] == '\0') {
return;
}
n = snprintf(cmd, sizeof cmd, "rm -rf -- '%s'", temp_dir);
if (n < 0 || (size_t)n >= sizeof cmd) {
return;
}
(void)system(cmd); /* best-effort; mkdtemp names are [A-Za-z0-9_]* */
temp_dir[0] = '\0';
}
/* Join temp_dir/`name` into `buf`; returns the snprintf result so callers
* can assert it without ever stringifying a %-carrying format (the munit
* %s-stringification trap, see .omo/notepads/stupidtools/learnings.md). */
static int
mkpath(char *buf, size_t sz, const char *name)
{
return snprintf(buf, sz, "%s/%s", temp_dir, name);
}
/* ---- probe construction ------------------------------------------------- */
/* Parse `src` as one check node, resolve its kind, build its probe spec
* into `*out` (fully heap-owned). Asserts each step succeeds and frees the
* document before returning. The caller frees via st_check_probe_free. */
static enum st_check_kind
build_from(const char *src, struct st_check_probe *out)
{
struct st_error *err = NULL;
struct st_kdl_document *doc = NULL;
struct st_kdl_node *node;
enum st_check_kind kind;
doc = st_kdl_parse(src, "t.kdl", &err);
munit_assert_null(err);
munit_assert_not_null(doc);
node = doc->nodes;
kind = st_check_kind_from_node(node, &err);
munit_assert_null(err);
munit_assert_int(kind, !=, ST_CHECK_KIND_COUNT);
munit_assert_null(st_check_probe_build(kind, node, out));
st_kdl_document_free(doc);
return kind;
}
/* ---- emit helpers ------------------------------------------------------ */
/* Emit one check's snippet into a temp file, return its bytes (NUL-
* terminated). Returns NULL on any failure. The buffer is static (munit
* forks per test, so it is never shared across tests). */
static const char *
emit_snippet_text(const char *checkname, const struct st_check_probe *p)
{
static char buf[8192];
char path[600];
FILE *f;
size_t n;
struct st_error *err;
int m;
m = mkpath(path, sizeof path, "snippet.sh");
if (m < 0) {
return NULL;
}
f = fopen(path, "w");
if (f == NULL) {
return NULL;
}
err = st_probe_emit_snippet(checkname, p, f);
fclose(f);
if (err != NULL) {
st_error_free(err);
return NULL;
}
f = fopen(path, "r");
if (f == NULL) {
return NULL;
}
n = fread(buf, 1, sizeof buf - 1, f);
fclose(f);
buf[n] = '\0';
return buf;
}
/* The configure-time environment the script's stub preamble plants. */
struct env {
const char *cc; /* CC value; NULL -> "cc" */
const char *cc_id; /* st_cc_id; NULL -> "gcc" */
const char *cross; /* "yes" -> cross_compiling=yes; else unset */
};
/* Write a runnable script: shebang, preamble assignments, the cache
* boilerplate, ONE snippet, and a dump of have_<name> + st_cross_<name>
* into have.out / cross.out under temp_dir. Returns 0 on success. */
static int
write_script(const char *checkname, const struct st_check_probe *p,
const struct env *env)
{
char path[600];
FILE *f;
struct st_error *err;
int m;
m = mkpath(path, sizeof path, "run.sh");
if (m < 0) {
return -1;
}
f = fopen(path, "w");
if (f == NULL) {
return -1;
}
(void)fprintf(f, "#!/bin/sh\n");
(void)fprintf(f, "CC='%s'\n",
env != NULL && env->cc != NULL ? env->cc : "cc");
(void)fprintf(f, "st_cc_id='%s'\n",
env != NULL && env->cc_id != NULL ? env->cc_id : "gcc");
if (env != NULL && env->cross != NULL && strcmp(env->cross, "yes") == 0) {
(void)fprintf(f, "cross_compiling=yes\n");
}
(void)fprintf(f, "config_cache='%s/config.cache'\n", temp_dir);
(void)fprintf(f, "config_log='%s/config.log'\n", temp_dir);
(void)fprintf(f, "st_tmpdir='%s'\n", temp_dir);
err = st_probe_emit_cache_functions(f);
if (err != NULL) {
st_error_free(err);
fclose(f);
return -1;
}
err = st_probe_emit_snippet(checkname, p, f);
if (err != NULL) {
st_error_free(err);
fclose(f);
return -1;
}
(void)fprintf(f, "printf '%%s\\n' \"$have_%s\" > '%s/have.out'\n",
checkname, temp_dir);
(void)fprintf(f, "printf '%%s\\n' \"${st_cross_%s:-}\" > '%s/cross.out'\n",
checkname, temp_dir);
fclose(f);
return 0;
}
/* Run "sh <path>" via system(); return the shell's exit status or -1. */
static int
run_sh(const char *path)
{
char cmd[700];
int rc;
int n = snprintf(cmd, sizeof cmd, "sh '%s'", path);
if (n < 0 || (size_t)n >= sizeof cmd) {
return -1;
}
rc = system(cmd);
if (rc == -1) {
return -1;
}
return WEXITSTATUS(rc);
}
/* As run_sh, but capture the script's stderr into `err_path`. */
static int
run_sh_err(const char *path, const char *err_path)
{
char cmd[1400];
int rc;
int n = snprintf(cmd, sizeof cmd, "sh '%s' 2>'%s'", path, err_path);
if (n < 0 || (size_t)n >= sizeof cmd) {
return -1;
}
rc = system(cmd);
if (rc == -1) {
return -1;
}
return WEXITSTATUS(rc);
}
/* Read a tiny result file under temp_dir, strip the trailing newline.
* Returns a static buffer (empty string when the file is missing). */
static const char *
read_result(const char *file)
{
static char buf[256];
char path[600];
FILE *f;
size_t n;
int m;
m = mkpath(path, sizeof path, file);
if (m < 0) {
return "";
}
f = fopen(path, "r");
if (f == NULL) {
return "";
}
n = fread(buf, 1, sizeof buf - 1, f);
fclose(f);
buf[n] = '\0';
buf[strcspn(buf, "\n")] = '\0';
return buf;
}
/* "<shell> -n <path>": return the shell's exit status or -1. */
static int
syntax_check(const char *shell, const char *path)
{
char cmd[700];
int rc;
int n = snprintf(cmd, sizeof cmd, "%s -n '%s'", shell, path);
if (n < 0 || (size_t)n >= sizeof cmd) {
return -1;
}
rc = system(cmd);
if (rc == -1) {
return -1;
}
return WEXITSTATUS(rc);
}
/* ---- (a) snippet content ---------------------------------------------- */
static MunitResult
test_header_snippet_content(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
struct st_check_probe p = { 0 };
const char *s;
(void)build_from("header \"unistd.h\"", &p);
munit_assert_int(p.mode, ==, ST_PROBE_COMPILE);
s = emit_snippet_text("unistd_h", &p);
munit_assert_not_null(s);
munit_assert_not_null(strstr(s, "$CC"));
munit_assert_not_null(strstr(s, "-c"));
munit_assert_not_null(strstr(s, "have_unistd_h"));
munit_assert_not_null(strstr(s, "have_unistd_h=yes"));
munit_assert_not_null(strstr(s, "have_unistd_h=no"));
munit_assert_not_null(strstr(s, "#include <unistd.h>"));
munit_assert_not_null(strstr(s, "ac_cv_unistd_h_$st_cc_id"));
munit_assert_not_null(strstr(s, "st_cache_get"));
munit_assert_not_null(strstr(s, "st_cache_set"));
st_check_probe_free(&p);
return MUNIT_OK;
}
/* ---- (b) real execution: yes and no ----------------------------------- */
static MunitResult
test_header_runs_yes_no(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
struct st_check_probe yes = { 0 };
struct st_check_probe no = { 0 };
char run[600];
int m;
(void)build_from("header \"unistd.h\"", &yes);
munit_assert_int(write_script("unistd_h", &yes, NULL), ==, 0);
m = mkpath(run, sizeof run, "run.sh");
munit_assert_int(m, >, 0);
munit_assert_int(run_sh(run), ==, 0);
munit_assert_string_equal(read_result("have.out"), "yes");
(void)build_from("header \"nope_missing_xyz.h\"", &no);
munit_assert_int(write_script("nope", &no, NULL), ==, 0);
munit_assert_int(run_sh(run), ==, 0);
munit_assert_string_equal(read_result("have.out"), "no");
st_check_probe_free(&yes);
st_check_probe_free(&no);
return MUNIT_OK;
}
/* ---- (c) RUN probe: cross-compile guard ------------------------------- */
static MunitResult
test_run_cross_guard(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
struct st_check_probe p = { 0 };
struct env cross = { "/bin/false", "gcc", "yes" };
const char *s;
char run[600];
char errp[600];
char logpath[600];
int m;
int rc;
(void)build_from("sizeof \"long\"", &p);
munit_assert_int(p.mode, ==, ST_PROBE_RUN);
/* static: the emitted snippet carries a cross_compiling guard */
s = emit_snippet_text("sz", &p);
munit_assert_not_null(s);
munit_assert_not_null(strstr(s, "cross_compiling"));
/* dynamic: cross_compiling=yes + a failing CC -> skip, no compile */
munit_assert_int(write_script("sz", &p, &cross), ==, 0);
m = mkpath(run, sizeof run, "run.sh");
munit_assert_int(m, >, 0);
m = mkpath(errp, sizeof errp, "cross.err");
munit_assert_int(m, >, 0);
rc = run_sh_err(run, errp);
munit_assert_int(rc, ==, 0);
munit_assert_string_equal(read_result("have.out"), "no");
munit_assert_string_equal(read_result("cross.out"), "yes");
/* the warning landed on stderr */
{
const char *e = read_result("cross.err");
munit_assert_true(strstr(e, "cross-compiling") != NULL);
}
/* no compile was attempted: config.log was never created */
m = mkpath(logpath, sizeof logpath, "config.log");
munit_assert_int(m, >, 0);
munit_assert_int(access(logpath, F_OK), ==, -1);
/* dynamic: cross_compiling unset -> actually compiles and runs */
munit_assert_int(write_script("sz", &p, NULL), ==, 0);
munit_assert_int(run_sh(run), ==, 0);
munit_assert_string_equal(read_result("have.out"), "yes");
munit_assert_string_equal(read_result("cross.out"), "");
/* the run output (sizeof value) landed in config.log */
munit_assert_int(access(logpath, F_OK), ==, 0);
st_check_probe_free(&p);
return MUNIT_OK;
}
/* ---- (d) COMMAND probe: argv quoting ---------------------------------- */
static MunitResult
test_command_probe_quoting(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
struct st_check_probe prog = { 0 };
struct st_check_probe pk = { 0 };
const char *s;
(void)build_from("program \"pkg-config\"", &prog);
s = emit_snippet_text("pgc", &prog);
munit_assert_not_null(s);
munit_assert_not_null(strstr(s, "command -v 'pkg-config'"));
(void)build_from("pkg_config \"openssl\"", &pk);
s = emit_snippet_text("ossl", &pk);
munit_assert_not_null(s);
munit_assert_not_null(strstr(s, "pkg-config --cflags --libs 'openssl'"));
st_check_probe_free(&prog);
st_check_probe_free(&pk);
return MUNIT_OK;
}
/* ---- (e) config.cache short-circuit ----------------------------------- */
static MunitResult
test_cache_short_circuit(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
struct st_check_probe p = { 0 };
struct env bad = { "/bin/false", "gcc", NULL };
char cache[600];
char run[600];
char logpath[600];
FILE *f;
int m;
int wr;
/* pre-seed the cache with a "yes" for a header that does NOT exist,
* then point CC at /bin/false: only a cache hit can yield yes. */
(void)build_from("header \"nope_missing_xyz.h\"", &p);
m = mkpath(cache, sizeof cache, "config.cache");
munit_assert_int(m, >, 0);
f = fopen(cache, "w");
munit_assert_not_null(f);
wr = fputs("ac_cv_cached_gcc=yes\n", f);
munit_assert_int(wr, !=, EOF);
fclose(f);
munit_assert_int(write_script("cached", &p, &bad), ==, 0);
m = mkpath(run, sizeof run, "run.sh");
munit_assert_int(m, >, 0);
munit_assert_int(run_sh(run), ==, 0);
munit_assert_string_equal(read_result("have.out"), "yes");
/* no probe was attempted (compiler never ran) */
m = mkpath(logpath, sizeof logpath, "config.log");
munit_assert_int(m, >, 0);
munit_assert_int(access(logpath, F_OK), ==, -1);
/* the cached line was consumed, not overwritten with "no" */
{
const char *c = read_result("config.cache");
munit_assert_not_null(strstr(c, "ac_cv_cached_gcc=yes"));
}
st_check_probe_free(&p);
return MUNIT_OK;
}
static MunitResult
test_cache_write(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
struct st_check_probe p = { 0 };
char run[600];
const char *c;
int m;
(void)build_from("header \"unistd.h\"", &p);
munit_assert_int(write_script("writecc", &p, NULL), ==, 0);
m = mkpath(run, sizeof run, "run.sh");
munit_assert_int(m, >, 0);
munit_assert_int(run_sh(run), ==, 0);
munit_assert_string_equal(read_result("have.out"), "yes");
c = read_result("config.cache");
munit_assert_not_null(strstr(c, "ac_cv_writecc_gcc=yes"));
st_check_probe_free(&p);
return MUNIT_OK;
}
/* ---- (f) syntax: sh -n / bash -n / zsh -n + banned-construct sweep ---- */
static MunitResult
test_syntax_and_banned(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
static const char *const banned[] = {
"[[ ", "]]", "local ", "==", "<<<", "&>", "set -e",
};
struct st_check_probe hdr = { 0 };
struct st_check_probe fn = { 0 };
struct st_check_probe sz = { 0 };
struct st_check_probe prog = { 0 };
char path[600];
char *bytes;
FILE *f;
long n;
size_t i;
int m;
(void)build_from("header \"unistd.h\"", &hdr);
(void)build_from("function \"strdup\"", &fn);
(void)build_from("sizeof \"long\"", &sz);
(void)build_from("program \"pkg-config\"", &prog);
m = mkpath(path, sizeof path, "all.sh");
munit_assert_int(m, >, 0);
f = fopen(path, "w");
munit_assert_not_null(f);
(void)fprintf(f, "#!/bin/sh\n");
munit_assert_null(st_probe_emit_cache_functions(f));
munit_assert_null(st_probe_emit_snippet("hdr", &hdr, f));
munit_assert_null(st_probe_emit_snippet("fn", &fn, f));
munit_assert_null(st_probe_emit_snippet("sz", &sz, f));
munit_assert_null(st_probe_emit_snippet("prog", &prog, f));
fclose(f);
munit_assert_int(syntax_check("sh", path), ==, 0);
munit_assert_int(syntax_check("bash", path), ==, 0);
munit_assert_int(syntax_check("zsh", path), ==, 0);
/* banned-construct sweep on the real emitted bytes */
f = fopen(path, "rb");
munit_assert_not_null(f);
munit_assert_int(fseek(f, 0, SEEK_END), ==, 0);
n = ftell(f);
munit_assert_int(n, >, 0);
munit_assert_int(fseek(f, 0, SEEK_SET), ==, 0);
bytes = munit_malloc((size_t)n + 1);
munit_assert_size(fread(bytes, 1, (size_t)n, f), ==, (size_t)n);
fclose(f);
bytes[n] = '\0';
for (i = 0; i < sizeof banned / sizeof banned[0]; i++) {
munit_assert_null(strstr(bytes, banned[i]));
}
free(bytes);
st_check_probe_free(&hdr);
st_check_probe_free(&fn);
st_check_probe_free(&sz);
st_check_probe_free(&prog);
return MUNIT_OK;
}
/* ---- (g) injection: hostile probe target stays inert ------------------ */
static MunitResult
test_injection_inert(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
char marker[600];
char csrc[700];
char run[600];
int m;
int rc;
struct st_check_probe p;
/* A C source that, if the shell ever interpreted it (unquoted), would
* remove `marker`'s directory. st_sh_quote makes it a literal; the
* compile just fails and the shell never executes the embedded rm. */
m = snprintf(marker, sizeof marker, "%s/victim.txt", temp_dir);
munit_assert_int(m, >, 0);
m = snprintf(csrc, sizeof csrc,
"#include <unistd.h>\n; rm -rf %s; \"\n", marker);
munit_assert_int(m, >, 0);
p = (struct st_check_probe){
.mode = ST_PROBE_COMPILE,
.c_source = csrc,
};
munit_assert_int(write_script("hostile", &p, NULL), ==, 0);
m = mkpath(run, sizeof run, "run.sh");
munit_assert_int(m, >, 0);
rc = run_sh(run);
munit_assert_int(rc, ==, 0);
munit_assert_string_equal(read_result("have.out"), "no");
munit_assert_int(access(marker, F_OK), ==, -1);
return MUNIT_OK;
}
/* ---- emit_all: a feature's list of checks ----------------------------- */
static MunitResult
test_emit_all(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
struct st_check_probe a = { .mode = ST_PROBE_COMPILE,
.c_source = "#include <unistd.h>\n" };
struct st_check_probe b = { .mode = ST_PROBE_COMPILE,
.c_source = "#include <stddef.h>\n" };
struct st_probe_entry entries[2];
char path[600];
FILE *f;
struct st_error *err;
char *bytes;
long n;
int m;
entries[0].name = "alpha";
entries[0].probe = &a;
entries[1].name = "beta";
entries[1].probe = &b;
m = mkpath(path, sizeof path, "all2.sh");
munit_assert_int(m, >, 0);
f = fopen(path, "w");
munit_assert_not_null(f);
munit_assert_null(st_probe_emit_cache_functions(f));
err = st_probe_emit_all(entries, 2, f);
munit_assert_null(err);
fclose(f);
f = fopen(path, "rb");
munit_assert_not_null(f);
munit_assert_int(fseek(f, 0, SEEK_END), ==, 0);
n = ftell(f);
munit_assert_int(fseek(f, 0, SEEK_SET), ==, 0);
bytes = munit_malloc((size_t)n + 1);
munit_assert_size(fread(bytes, 1, (size_t)n, f), ==, (size_t)n);
fclose(f);
bytes[n] = '\0';
munit_assert_not_null(strstr(bytes, "have_alpha"));
munit_assert_not_null(strstr(bytes, "have_beta"));
munit_assert_not_null(strstr(bytes, "ac_cv_alpha_$st_cc_id"));
munit_assert_not_null(strstr(bytes, "ac_cv_beta_$st_cc_id"));
free(bytes);
/* empty list is a clean no-op */
f = tmpfile();
munit_assert_not_null(f);
err = st_probe_emit_all(NULL, 0, f);
munit_assert_null(err);
fclose(f);
return MUNIT_OK;
}
/* ---- error handling ---------------------------------------------------- */
static MunitResult
test_errors(const MunitParameter params[], void *data)
{
(void)params;
(void)data;
struct st_check_probe good = { .mode = ST_PROBE_COMPILE,
.c_source = "#include <x.h>\n" };
struct st_check_probe nocmd = { .mode = ST_PROBE_COMMAND,
.command = NULL };
struct st_check_probe nosrc = { .mode = ST_PROBE_COMPILE,
.c_source = NULL };
struct st_check_probe badmode = { .mode = (enum st_check_probe_mode)999,
.c_source = "x" };
FILE *f = tmpfile();
struct st_error *err;
munit_assert_not_null(f);
err = st_probe_emit_snippet(NULL, &good, f);
munit_assert_not_null(err);
munit_assert_int(st_error_category_of(err), ==, ST_ERR_USAGE);
st_error_free(err);
err = st_probe_emit_snippet("bad name", &good, f);
munit_assert_not_null(err);
munit_assert_int(st_error_category_of(err), ==, ST_ERR_USAGE);
st_error_free(err);
err = st_probe_emit_snippet("ok", NULL, f);
munit_assert_not_null(err);
st_error_free(err);
err = st_probe_emit_snippet("ok", &good, NULL);
munit_assert_not_null(err);
st_error_free(err);
err = st_probe_emit_snippet("ok", &badmode, f);
munit_assert_not_null(err);
munit_assert_int(st_error_category_of(err), ==, ST_ERR_USAGE);
st_error_free(err);
err = st_probe_emit_snippet("ok", &nocmd, f);
munit_assert_not_null(err);
munit_assert_int(st_error_category_of(err), ==, ST_ERR_USAGE);
st_error_free(err);
err = st_probe_emit_snippet("ok", &nosrc, f);
munit_assert_not_null(err);
munit_assert_int(st_error_category_of(err), ==, ST_ERR_USAGE);
st_error_free(err);
err = st_probe_emit_cache_functions(NULL);
munit_assert_not_null(err);
st_error_free(err);
/* a valid emit returns no error */
err = st_probe_emit_snippet("ok", &good, f);
munit_assert_null(err);
fclose(f);
return MUNIT_OK;
}
static MunitTest tests[] = {
{ "/probe/header-snippet-content", test_header_snippet_content, setup,
teardown, MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/header-runs-yes-no", test_header_runs_yes_no, setup, teardown,
MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/run-cross-guard", test_run_cross_guard, setup, teardown,
MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/command-quoting", test_command_probe_quoting, setup, teardown,
MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/cache-short-circuit", test_cache_short_circuit, setup, teardown,
MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/cache-write", test_cache_write, setup, teardown,
MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/syntax-banned", test_syntax_and_banned, setup, teardown,
MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/injection-inert", test_injection_inert, setup, teardown,
MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/emit-all", test_emit_all, setup, teardown,
MUNIT_TEST_OPTION_NONE, NULL },
{ "/probe/errors", test_errors, NULL, NULL, MUNIT_TEST_OPTION_NONE,
NULL },
{ NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
};
static const MunitSuite suite = {
"/probe", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE,
};
int
main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)])
{
return munit_suite_main(&suite, NULL, argc, argv);
}