Template
Extension discovery (plan todo 20): scans --ext-dir dirs, STUPIDTOOLS_EXT, the XDG user dir and a builtin-ext dir for *.lua modules, runs each in the sandboxed Lua runtime, and enumerates its registrations; the builtin C/C++ modules load first via st_ext_init_builtins. Fail-fast with an error naming file and line on any malformed/sandbox-violating module. The CLI gains a repeatable --ext-dir flag; with flag or env present main.c runs discovery and prints 'loaded extension: ...' lines from actual registrations (interim wiring until todo 23). Note: src/Makefile.am gained the ext + vendored Lua sources because the binary otherwise cannot link discovery -- required for the root-verifiable CLI acceptance.
562 lines
16 KiB
C
562 lines
16 KiB
C
/* LINK: ../../src/ext/discovery.c ../../src/ext/lua.c ../../src/ext/abi.c ../../src/ext/lang_c.c ../../src/ext/lang_cpp.c ../../src/error.c ../../src/span.c ../../thirdparty/lua/lapi.c ../../thirdparty/lua/lauxlib.c ../../thirdparty/lua/lbaselib.c ../../thirdparty/lua/lcode.c ../../thirdparty/lua/lctype.c ../../thirdparty/lua/ldebug.c ../../thirdparty/lua/ldo.c ../../thirdparty/lua/ldump.c ../../thirdparty/lua/lfunc.c ../../thirdparty/lua/lgc.c ../../thirdparty/lua/llex.c ../../thirdparty/lua/lmem.c ../../thirdparty/lua/lobject.c ../../thirdparty/lua/lopcodes.c ../../thirdparty/lua/lparser.c ../../thirdparty/lua/lstate.c ../../thirdparty/lua/lstring.c ../../thirdparty/lua/ltable.c ../../thirdparty/lua/ltm.c ../../thirdparty/lua/lundump.c ../../thirdparty/lua/lvm.c ../../thirdparty/lua/lzio.c ../../thirdparty/lua/lstrlib.c ../../thirdparty/lua/ltablib.c */
|
|
|
|
#ifndef _POSIX_C_SOURCE
|
|
#define _POSIX_C_SOURCE 200809L /* mkdtemp, strdup */
|
|
#endif
|
|
|
|
/*
|
|
* tests/unit/test_discovery.c
|
|
*
|
|
* Unit tests for extension discovery and loading (src/ext/discovery.h,
|
|
* plan todo 20).
|
|
*
|
|
* The magic LINK comment on line 1 is REQUIRED by tests/run.sh: it
|
|
* lists the extra .c sources compiled into this test binary. It links
|
|
* discovery.c plus everything it needs -- the sandboxed Lua runtime
|
|
* (lua.c + the same 27 vendored Lua sources as test_lua.c, minus
|
|
* linit/lmathlib/loadlib/liolib/loslib/lua.c/luac.c, see the -lm note
|
|
* there), the extension ABI + builtin C/C++ modules, and error/span.
|
|
*
|
|
* Environment isolation: every test points XDG_DATA_HOME at an empty
|
|
* temp dir and unsets STUPIDTOOLS_EXT first, so host state can never
|
|
* leak into discovery. The builtin directory (builtin-ext relative to
|
|
* the cwd) ships no .lua modules in this todo, so it is a no-op as
|
|
* well; note that exact runtime-count assertions below assume that.
|
|
*/
|
|
#include "munit.h"
|
|
|
|
#include "ext/abi.h"
|
|
#include "ext/discovery.h"
|
|
#include "ext/lua.h"
|
|
|
|
#include <dirent.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
/* ---------- filesystem helpers ---------- */
|
|
|
|
static char *
|
|
make_temp_dir(void)
|
|
{
|
|
char *d = strdup("/tmp/st_discovery_XXXXXX");
|
|
|
|
if (d == NULL || mkdtemp(d) == NULL) {
|
|
free(d);
|
|
return NULL;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
static int
|
|
write_file(const char *dir, const char *name, const char *content)
|
|
{
|
|
char path[1024];
|
|
FILE *f;
|
|
size_t len = strlen(content);
|
|
|
|
if (snprintf(path, sizeof path, "%s/%s", dir, name) >=
|
|
(int)sizeof path) {
|
|
return -1;
|
|
}
|
|
f = fopen(path, "wb");
|
|
if (f == NULL) return -1;
|
|
if (fwrite(content, 1, len, f) != len) {
|
|
(void)fclose(f);
|
|
return -1;
|
|
}
|
|
return fclose(f) == 0 ? 0 : -1;
|
|
}
|
|
|
|
static int
|
|
write_bytes(const char *dir, const char *name, const unsigned char *bytes,
|
|
size_t len)
|
|
{
|
|
char path[1024];
|
|
FILE *f;
|
|
|
|
if (snprintf(path, sizeof path, "%s/%s", dir, name) >=
|
|
(int)sizeof path) {
|
|
return -1;
|
|
}
|
|
f = fopen(path, "wb");
|
|
if (f == NULL) return -1;
|
|
if (fwrite(bytes, 1, len, f) != len) {
|
|
(void)fclose(f);
|
|
return -1;
|
|
}
|
|
return fclose(f) == 0 ? 0 : -1;
|
|
}
|
|
|
|
/* Recursive delete (plain POSIX: unlink files, rmdir dirs). */
|
|
static void
|
|
rm_rf(const char *path)
|
|
{
|
|
struct stat st;
|
|
DIR *d;
|
|
struct dirent *e;
|
|
|
|
if (lstat(path, &st) != 0) return;
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
(void)unlink(path);
|
|
return;
|
|
}
|
|
d = opendir(path);
|
|
if (d == NULL) return;
|
|
while ((e = readdir(d)) != NULL) {
|
|
char sub[1024];
|
|
|
|
if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0)
|
|
continue;
|
|
if (snprintf(sub, sizeof sub, "%s/%s", path, e->d_name) >=
|
|
(int)sizeof sub)
|
|
continue;
|
|
rm_rf(sub);
|
|
}
|
|
(void)closedir(d);
|
|
(void)rmdir(path);
|
|
}
|
|
|
|
/* Fresh ctx + rt pair for one discovery run (discovery is one-shot per
|
|
* ctx: it re-runs the builtin init, so a second call would fail on
|
|
* duplicate registrations). */
|
|
static int
|
|
fresh_pair(struct st_ext_ctx **ctx, struct st_lua_rt **rt)
|
|
{
|
|
*ctx = st_ext_ctx_new();
|
|
*rt = st_lua_rt_new();
|
|
return *ctx != NULL && *rt != NULL ? 0 : -1;
|
|
}
|
|
|
|
/* ---------- (a) a valid module registers into the runtime ---------- */
|
|
|
|
static MunitResult
|
|
test_valid_module(const MunitParameter params[], void *data)
|
|
{
|
|
char *xdg = NULL;
|
|
char *ext = NULL;
|
|
struct st_ext_ctx *ctx = NULL;
|
|
struct st_lua_rt *rt = NULL;
|
|
struct st_error *err;
|
|
const char *dirs[1];
|
|
|
|
(void)params;
|
|
(void)data;
|
|
|
|
xdg = make_temp_dir();
|
|
ext = make_temp_dir();
|
|
munit_assert_not_null(xdg);
|
|
munit_assert_not_null(ext);
|
|
munit_assert_int(setenv("XDG_DATA_HOME", xdg, 1), ==, 0);
|
|
munit_assert_int(unsetenv("STUPIDTOOLS_EXT"), ==, 0);
|
|
|
|
munit_assert_int(write_file(ext, "hello.lua",
|
|
"st.register_check(\"magic\")\n"
|
|
"st.register_language(\"fortran\")\n"), ==, 0);
|
|
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
|
|
dirs[0] = ext;
|
|
err = st_ext_discover(ctx, rt, dirs, 1);
|
|
munit_assert_null(err);
|
|
|
|
/* the module's registrations are enumerable from the runtime */
|
|
munit_assert_size(st_lua_check_count(rt), ==, 1);
|
|
munit_assert_string_equal(st_lua_check_name(rt, 0), "magic");
|
|
munit_assert_size(st_lua_language_count(rt), ==, 1);
|
|
munit_assert_string_equal(st_lua_language_name(rt, 0), "fortran");
|
|
|
|
/* the builtin C/C++ modules landed in the ABI ctx */
|
|
munit_assert_size(st_ext_language_count(ctx), ==, 2);
|
|
munit_assert_string_equal(st_ext_language_name(ctx, 0), "c");
|
|
munit_assert_string_equal(st_ext_language_name(ctx, 1), "cxx");
|
|
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
rm_rf(ext);
|
|
rm_rf(xdg);
|
|
free(ext);
|
|
free(xdg);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
/* ---------- (b) malformed module: clear error naming the file, fail-fast ---------- */
|
|
|
|
static MunitResult
|
|
test_malformed_fail_fast(const MunitParameter params[], void *data)
|
|
{
|
|
char *xdg = NULL;
|
|
char *ext = NULL;
|
|
struct st_ext_ctx *ctx = NULL;
|
|
struct st_lua_rt *rt = NULL;
|
|
struct st_error *err;
|
|
const char *msg;
|
|
const char *dirs[1];
|
|
|
|
(void)params;
|
|
(void)data;
|
|
|
|
xdg = make_temp_dir();
|
|
ext = make_temp_dir();
|
|
munit_assert_not_null(xdg);
|
|
munit_assert_not_null(ext);
|
|
munit_assert_int(setenv("XDG_DATA_HOME", xdg, 1), ==, 0);
|
|
munit_assert_int(unsetenv("STUPIDTOOLS_EXT"), ==, 0);
|
|
|
|
munit_assert_int(write_file(ext, "bad.lua", "local x =\n"), ==, 0);
|
|
munit_assert_int(write_file(ext, "good.lua",
|
|
"st.register_check(\"never\")\n"), ==, 0);
|
|
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
|
|
dirs[0] = ext;
|
|
err = st_ext_discover(ctx, rt, dirs, 1);
|
|
munit_assert_not_null(err);
|
|
munit_assert_int(st_error_category_of(err), ==, ST_ERR_INTERNAL);
|
|
msg = st_error_message(err);
|
|
munit_assert_not_null(msg);
|
|
munit_assert_not_null(strstr(msg, "extension load failed"));
|
|
munit_assert_not_null(strstr(msg, "bad.lua"));
|
|
munit_assert_not_null(strstr(msg, ":2:")); /* file AND line named */
|
|
|
|
/* fail-fast: good.lua (which sorts after bad.lua) never ran */
|
|
munit_assert_size(st_lua_check_count(rt), ==, 0);
|
|
st_error_free(err);
|
|
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
rm_rf(ext);
|
|
rm_rf(xdg);
|
|
free(ext);
|
|
free(xdg);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
/* ---------- (c) a sandbox violation yields the sandbox error ---------- */
|
|
|
|
static MunitResult
|
|
test_sandbox_violation(const MunitParameter params[], void *data)
|
|
{
|
|
char *xdg = NULL;
|
|
char *ext = NULL;
|
|
struct st_ext_ctx *ctx = NULL;
|
|
struct st_lua_rt *rt = NULL;
|
|
struct st_error *err;
|
|
const char *msg;
|
|
const char *dirs[1];
|
|
|
|
(void)params;
|
|
(void)data;
|
|
|
|
xdg = make_temp_dir();
|
|
ext = make_temp_dir();
|
|
munit_assert_not_null(xdg);
|
|
munit_assert_not_null(ext);
|
|
munit_assert_int(setenv("XDG_DATA_HOME", xdg, 1), ==, 0);
|
|
munit_assert_int(unsetenv("STUPIDTOOLS_EXT"), ==, 0);
|
|
|
|
munit_assert_int(write_file(ext, "evil.lua", "os.execute(\"id\")\n"),
|
|
==, 0);
|
|
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
|
|
dirs[0] = ext;
|
|
err = st_ext_discover(ctx, rt, dirs, 1);
|
|
munit_assert_not_null(err);
|
|
munit_assert_int(st_error_category_of(err), ==, ST_ERR_KDL_SCHEMA);
|
|
msg = st_error_message(err);
|
|
munit_assert_not_null(msg);
|
|
munit_assert_not_null(strstr(msg, "sandbox blocked"));
|
|
munit_assert_not_null(strstr(msg, "evil.lua"));
|
|
st_error_free(err);
|
|
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
rm_rf(ext);
|
|
rm_rf(xdg);
|
|
free(ext);
|
|
free(xdg);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
/* ---------- (d) empty dir / missing dir / NULL dirs: clean no-op ---------- */
|
|
|
|
static MunitResult
|
|
test_empty_and_missing_dirs(const MunitParameter params[], void *data)
|
|
{
|
|
char *xdg = NULL;
|
|
char *empty = NULL;
|
|
struct st_ext_ctx *ctx = NULL;
|
|
struct st_lua_rt *rt = NULL;
|
|
struct st_error *err;
|
|
const char *dirs[2];
|
|
|
|
(void)params;
|
|
(void)data;
|
|
|
|
xdg = make_temp_dir();
|
|
empty = make_temp_dir();
|
|
munit_assert_not_null(xdg);
|
|
munit_assert_not_null(empty);
|
|
munit_assert_int(setenv("XDG_DATA_HOME", xdg, 1), ==, 0);
|
|
munit_assert_int(unsetenv("STUPIDTOOLS_EXT"), ==, 0);
|
|
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
|
|
/* a nonexistent dir and an empty dir are both silent no-ops */
|
|
dirs[0] = "/nonexistent/st_discovery_dir";
|
|
dirs[1] = empty;
|
|
err = st_ext_discover(ctx, rt, dirs, 2);
|
|
munit_assert_null(err);
|
|
munit_assert_size(st_lua_check_count(rt), ==, 0);
|
|
munit_assert_size(st_lua_language_count(rt), ==, 0);
|
|
munit_assert_size(st_ext_language_count(ctx), ==, 2); /* builtins only */
|
|
|
|
/* NULL/0 extra dirs are valid too (env + user + builtin only) */
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
err = st_ext_discover(ctx, rt, NULL, 0);
|
|
munit_assert_null(err);
|
|
munit_assert_size(st_lua_check_count(rt), ==, 0);
|
|
|
|
/* discovery is one-shot per ctx: the builtin init re-runs and
|
|
* duplicate registrations fail (documented contract) */
|
|
err = st_ext_discover(ctx, rt, NULL, 0);
|
|
munit_assert_not_null(err);
|
|
st_error_free(err);
|
|
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
rm_rf(empty);
|
|
rm_rf(xdg);
|
|
free(empty);
|
|
free(xdg);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
/* ---------- (e) a binary file named *.lua yields a load error ---------- */
|
|
|
|
static MunitResult
|
|
test_binary_file(const MunitParameter params[], void *data)
|
|
{
|
|
static const unsigned char elf_head[] = { 0x7f, 'E', 'L', 'F', 0x02,
|
|
0x01, 0x01 };
|
|
char *xdg = NULL;
|
|
char *ext = NULL;
|
|
struct st_ext_ctx *ctx = NULL;
|
|
struct st_lua_rt *rt = NULL;
|
|
struct st_error *err;
|
|
const char *msg;
|
|
const char *dirs[1];
|
|
|
|
(void)params;
|
|
(void)data;
|
|
|
|
xdg = make_temp_dir();
|
|
ext = make_temp_dir();
|
|
munit_assert_not_null(xdg);
|
|
munit_assert_not_null(ext);
|
|
munit_assert_int(setenv("XDG_DATA_HOME", xdg, 1), ==, 0);
|
|
munit_assert_int(unsetenv("STUPIDTOOLS_EXT"), ==, 0);
|
|
|
|
munit_assert_int(write_bytes(ext, "bin.lua", elf_head,
|
|
sizeof elf_head), ==, 0);
|
|
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
|
|
dirs[0] = ext;
|
|
err = st_ext_discover(ctx, rt, dirs, 1);
|
|
munit_assert_not_null(err);
|
|
msg = st_error_message(err);
|
|
munit_assert_not_null(msg);
|
|
munit_assert_not_null(strstr(msg, "extension load failed"));
|
|
munit_assert_not_null(strstr(msg, "bin.lua"));
|
|
st_error_free(err);
|
|
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
rm_rf(ext);
|
|
rm_rf(xdg);
|
|
free(ext);
|
|
free(xdg);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
/* ---------- (f) non-.lua entries (and a DIR named *.lua) are ignored ---------- */
|
|
|
|
static MunitResult
|
|
test_ignores_non_lua(const MunitParameter params[], void *data)
|
|
{
|
|
char *xdg = NULL;
|
|
char *ext = NULL;
|
|
char sub[1024];
|
|
int sub_len;
|
|
struct st_ext_ctx *ctx = NULL;
|
|
struct st_lua_rt *rt = NULL;
|
|
struct st_error *err;
|
|
const char *dirs[1];
|
|
|
|
(void)params;
|
|
(void)data;
|
|
|
|
xdg = make_temp_dir();
|
|
ext = make_temp_dir();
|
|
munit_assert_not_null(xdg);
|
|
munit_assert_not_null(ext);
|
|
munit_assert_int(setenv("XDG_DATA_HOME", xdg, 1), ==, 0);
|
|
munit_assert_int(unsetenv("STUPIDTOOLS_EXT"), ==, 0);
|
|
|
|
munit_assert_int(write_file(ext, "notes.txt",
|
|
"st.register_check(\"hidden\")\n"), ==, 0);
|
|
sub_len = snprintf(sub, sizeof sub, "%s/sub.lua", ext);
|
|
munit_assert_int(sub_len, >=, 0);
|
|
munit_assert_int(mkdir(sub, 0777), ==, 0);
|
|
munit_assert_int(write_file(ext, "sub.lua/inner.lua",
|
|
"st.register_check(\"also-hidden\")\n"), ==, 0);
|
|
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
|
|
dirs[0] = ext;
|
|
err = st_ext_discover(ctx, rt, dirs, 1);
|
|
munit_assert_null(err);
|
|
munit_assert_size(st_lua_check_count(rt), ==, 0);
|
|
munit_assert_size(st_lua_language_count(rt), ==, 0);
|
|
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
rm_rf(ext);
|
|
rm_rf(xdg);
|
|
free(ext);
|
|
free(xdg);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
/* ---------- (g) modules load in bytewise-lexicographic order ---------- */
|
|
|
|
static MunitResult
|
|
test_deterministic_order(const MunitParameter params[], void *data)
|
|
{
|
|
char *xdg = NULL;
|
|
char *ext = NULL;
|
|
struct st_ext_ctx *ctx = NULL;
|
|
struct st_lua_rt *rt = NULL;
|
|
struct st_error *err;
|
|
const char *dirs[1];
|
|
|
|
(void)params;
|
|
(void)data;
|
|
|
|
xdg = make_temp_dir();
|
|
ext = make_temp_dir();
|
|
munit_assert_not_null(xdg);
|
|
munit_assert_not_null(ext);
|
|
munit_assert_int(setenv("XDG_DATA_HOME", xdg, 1), ==, 0);
|
|
munit_assert_int(unsetenv("STUPIDTOOLS_EXT"), ==, 0);
|
|
|
|
/* written in reverse order on purpose: discovery must sort */
|
|
munit_assert_int(write_file(ext, "b.lua",
|
|
"st.register_check(\"second\")\n"), ==, 0);
|
|
munit_assert_int(write_file(ext, "a.lua",
|
|
"st.register_check(\"first\")\n"), ==, 0);
|
|
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
|
|
dirs[0] = ext;
|
|
err = st_ext_discover(ctx, rt, dirs, 1);
|
|
munit_assert_null(err);
|
|
munit_assert_size(st_lua_check_count(rt), ==, 2);
|
|
munit_assert_string_equal(st_lua_check_name(rt, 0), "first");
|
|
munit_assert_string_equal(st_lua_check_name(rt, 1), "second");
|
|
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
rm_rf(ext);
|
|
rm_rf(xdg);
|
|
free(ext);
|
|
free(xdg);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
/* ---------- (h) STUPIDTOOLS_EXT is honored, after the explicit dirs ---------- */
|
|
|
|
static MunitResult
|
|
test_env_dirs(const MunitParameter params[], void *data)
|
|
{
|
|
char *xdg = NULL;
|
|
char *ext1 = NULL;
|
|
char *ext2 = NULL;
|
|
struct st_ext_ctx *ctx = NULL;
|
|
struct st_lua_rt *rt = NULL;
|
|
struct st_error *err;
|
|
const char *dirs[1];
|
|
|
|
(void)params;
|
|
(void)data;
|
|
|
|
xdg = make_temp_dir();
|
|
ext1 = make_temp_dir();
|
|
ext2 = make_temp_dir();
|
|
munit_assert_not_null(xdg);
|
|
munit_assert_not_null(ext1);
|
|
munit_assert_not_null(ext2);
|
|
munit_assert_int(setenv("XDG_DATA_HOME", xdg, 1), ==, 0);
|
|
munit_assert_int(setenv("STUPIDTOOLS_EXT", ext2, 1), ==, 0);
|
|
|
|
munit_assert_int(write_file(ext1, "one.lua",
|
|
"st.register_check(\"one\")\n"), ==, 0);
|
|
munit_assert_int(write_file(ext2, "two.lua",
|
|
"st.register_check(\"two\")\n"), ==, 0);
|
|
|
|
munit_assert_int(fresh_pair(&ctx, &rt), ==, 0);
|
|
|
|
dirs[0] = ext1;
|
|
err = st_ext_discover(ctx, rt, dirs, 1);
|
|
munit_assert_null(err);
|
|
munit_assert_size(st_lua_check_count(rt), ==, 2);
|
|
munit_assert_string_equal(st_lua_check_name(rt, 0), "one");
|
|
munit_assert_string_equal(st_lua_check_name(rt, 1), "two");
|
|
|
|
st_lua_rt_free(rt);
|
|
st_ext_ctx_free(ctx);
|
|
rm_rf(ext1);
|
|
rm_rf(ext2);
|
|
rm_rf(xdg);
|
|
free(ext1);
|
|
free(ext2);
|
|
free(xdg);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
static MunitTest tests[] = {
|
|
{ "/discovery/valid-module", test_valid_module, NULL, NULL,
|
|
MUNIT_TEST_OPTION_NONE, NULL },
|
|
{ "/discovery/malformed-fail-fast", test_malformed_fail_fast, NULL,
|
|
NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
|
{ "/discovery/sandbox-violation", test_sandbox_violation, NULL, NULL,
|
|
MUNIT_TEST_OPTION_NONE, NULL },
|
|
{ "/discovery/empty-and-missing-dirs", test_empty_and_missing_dirs,
|
|
NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
|
{ "/discovery/binary-file", test_binary_file, NULL, NULL,
|
|
MUNIT_TEST_OPTION_NONE, NULL },
|
|
{ "/discovery/ignores-non-lua", test_ignores_non_lua, NULL, NULL,
|
|
MUNIT_TEST_OPTION_NONE, NULL },
|
|
{ "/discovery/deterministic-order", test_deterministic_order, NULL,
|
|
NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
|
{ "/discovery/env-dirs", test_env_dirs, NULL, NULL,
|
|
MUNIT_TEST_OPTION_NONE, NULL },
|
|
{ NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
|
};
|
|
|
|
static const MunitSuite suite = {
|
|
"/discovery", 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);
|
|
}
|