Template
feat(kdl): add DSL schema validation
This commit is contained in:
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
/* stupid.kdl - the canonical stupidtools build file (todo 9 fixture).
|
||||
|
||||
Pins the stupidtools DSL grammar (enforced by src/kdl/schema.h):
|
||||
|
||||
project "name" version "semver" required, MUST be the first node
|
||||
target "name" { src "file" ... ; feature "name" ... }
|
||||
feature "name" { <checks> } checks: structural until todo 10
|
||||
option "name" default=#bool default is an optional bool property
|
||||
|
||||
This is the VALID fixture for tests/unit/test_schema.c and is reused by
|
||||
todo 16 (configure generation) and todo 23 (self-host), so it is
|
||||
representative of a real build file, not a minimal toy. */
|
||||
|
||||
// The project node: the name is the first positional argument; the
|
||||
// version string follows the literal `version` keyword.
|
||||
project "stupidtools" version "1.0.0"
|
||||
|
||||
// Features bundle the checks a target depends on. `header` and `library`
|
||||
// are two of the 8 check kinds (todo 10 defines them all); here they are
|
||||
// structural placeholders.
|
||||
feature "pthread" {
|
||||
header "pthread.h"
|
||||
library "pthread"
|
||||
}
|
||||
|
||||
feature "math" {
|
||||
library "m"
|
||||
}
|
||||
|
||||
// A target: the sources that make up one artifact, plus the features it
|
||||
// needs at build/link time.
|
||||
target "default" {
|
||||
src "src/main.c"
|
||||
src "src/cli.c"
|
||||
src "src/kdl/lexer.c"
|
||||
src "src/kdl/parser.c"
|
||||
feature "pthread"
|
||||
feature "math"
|
||||
}
|
||||
|
||||
// An option maps to --enable-debug / --disable-debug; `default` is an
|
||||
// optional boolean property (unannotated #true/#false only).
|
||||
option "debug" default=#false
|
||||
@@ -0,0 +1,529 @@
|
||||
/* LINK: ../../src/kdl/schema.c ../../src/kdl/value.c ../../src/kdl/parser.c ../../src/kdl/lexer.c ../../src/error.c ../../src/span.c */
|
||||
/* tests/unit/test_schema.c
|
||||
*
|
||||
* Unit tests for the stupidtools DSL schema validator (todo 9).
|
||||
*
|
||||
* The magic LINK comment on line 1 is REQUIRED by tests/run.sh: it lists
|
||||
* the extra .c sources to compile into this test binary (paths relative
|
||||
* to tests/unit/, space-separated). schema.c depends on value.c (typed
|
||||
* value interpretation) and error.c (typed errors); parser.c + lexer.c
|
||||
* are linked so documents can be built end-to-end from source text, and
|
||||
* span.c via error.c's st_span_print.
|
||||
*
|
||||
* The grammar under test is pinned in src/kdl/schema.h. Assertions check
|
||||
* REAL error properties: category (always ST_ERR_KDL_SCHEMA), message
|
||||
* text (must name the offending node), and exact line/col spans — never
|
||||
* just "returns an error".
|
||||
*/
|
||||
#include "munit.h"
|
||||
|
||||
#include "error.h"
|
||||
#include "kdl/ast.h"
|
||||
#include "kdl/schema.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ---- fixture loading -------------------------------------------------- */
|
||||
|
||||
/* Locate tests/fixtures/stupid.kdl. The harness runs the test binary with
|
||||
* cwd = repo top (make check) or tests/unit (manual run); probe both. */
|
||||
static const char *
|
||||
fixture_path(void)
|
||||
{
|
||||
static const char *const candidates[] = {
|
||||
"tests/fixtures/stupid.kdl",
|
||||
"../fixtures/stupid.kdl",
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(candidates) / sizeof(candidates[0]); i++) {
|
||||
FILE *f = fopen(candidates[i], "rb");
|
||||
if (f != NULL) {
|
||||
fclose(f);
|
||||
return candidates[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Slurp the fixture into a NUL-terminated buffer (the lexer requires NUL
|
||||
* termination). Returns NULL on any I/O failure. Caller frees. */
|
||||
static char *
|
||||
slurp_fixture(const char *path)
|
||||
{
|
||||
FILE *f;
|
||||
long n;
|
||||
char *buf;
|
||||
|
||||
if (path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
f = fopen(path, "rb");
|
||||
if (f == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (fseek(f, 0, SEEK_END) != 0) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
n = ftell(f);
|
||||
if (n < 0 || fseek(f, 0, SEEK_SET) != 0) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
buf = munit_malloc((size_t)n + 1);
|
||||
if (fread(buf, 1, (size_t)n, f) != (size_t)n) {
|
||||
free(buf);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
fclose(f);
|
||||
buf[n] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ---- helpers ---------------------------------------------------------- */
|
||||
|
||||
/* Parse `src` (asserting parse success) and validate; assert the result
|
||||
* is a ST_ERR_KDL_SCHEMA error whose message contains `needle` and whose
|
||||
* span is exactly (line, col). */
|
||||
static void
|
||||
assert_schema_error(const char *src, const char *needle, size_t line,
|
||||
size_t col)
|
||||
{
|
||||
struct st_error *err = NULL;
|
||||
struct st_kdl_document *doc = st_kdl_parse(src, "t.kdl", &err);
|
||||
|
||||
munit_assert_null(err);
|
||||
munit_assert_not_null(doc);
|
||||
err = st_kdl_validate(doc);
|
||||
munit_assert_not_null(err);
|
||||
munit_assert_int(st_error_category_of(err), ==, ST_ERR_KDL_SCHEMA);
|
||||
munit_assert_not_null(err->span);
|
||||
munit_assert_size(err->span->line, ==, line);
|
||||
munit_assert_size(err->span->col, ==, col);
|
||||
munit_assert_true(strstr(st_error_message(err), needle) != NULL);
|
||||
st_error_free(err);
|
||||
st_kdl_document_free(doc);
|
||||
}
|
||||
|
||||
/* Parse `src` (asserting parse success) and validate; assert success. */
|
||||
static void
|
||||
assert_valid(const char *src)
|
||||
{
|
||||
struct st_error *err = NULL;
|
||||
struct st_kdl_document *doc = st_kdl_parse(src, "t.kdl", &err);
|
||||
|
||||
munit_assert_null(err);
|
||||
munit_assert_not_null(doc);
|
||||
err = st_kdl_validate(doc);
|
||||
munit_assert_null(err);
|
||||
st_kdl_document_free(doc);
|
||||
}
|
||||
|
||||
/* ---- tests ------------------------------------------------------------ */
|
||||
|
||||
/* (a) tests/fixtures/stupid.kdl validates clean and carries the expected
|
||||
* canonical shapes: project first (name + version keyword + semver), a
|
||||
* target with src/feature children, features with header/library check
|
||||
* placeholders, and an option with a bool default. */
|
||||
static MunitResult
|
||||
test_valid_fixture(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
const char *path = fixture_path();
|
||||
char *src;
|
||||
struct st_error *err = NULL;
|
||||
struct st_kdl_document *doc;
|
||||
struct st_kdl_node *n;
|
||||
size_t top_count = 0;
|
||||
size_t target_children = 0;
|
||||
|
||||
munit_assert_not_null(path);
|
||||
src = slurp_fixture(path);
|
||||
munit_assert_not_null(src);
|
||||
|
||||
doc = st_kdl_parse(src, path, &err);
|
||||
munit_assert_not_null(doc);
|
||||
munit_assert_null(err);
|
||||
|
||||
/* validates clean (the core acceptance of this todo) */
|
||||
munit_assert_null(st_kdl_validate(doc));
|
||||
|
||||
/* five top-level nodes: project, feature, feature, target, option */
|
||||
for (n = doc->nodes; n != NULL; n = n->next) {
|
||||
top_count++;
|
||||
}
|
||||
munit_assert_size(top_count, ==, 5);
|
||||
|
||||
/* project spans (hand-verified against cat -n of the fixture) */
|
||||
n = doc->nodes;
|
||||
munit_assert_size(n->name.span.line, ==, 16);
|
||||
munit_assert_size(n->name.span.col, ==, 1);
|
||||
munit_assert_not_null(n->args);
|
||||
munit_assert_size(n->args->value.span.col, ==, 9); /* "stupidtools" */
|
||||
munit_assert_size(n->args->next->value.span.col, ==, 23); /* version kw */
|
||||
munit_assert_size(n->args->next->next->value.span.col, ==, 31); /* "1.0.0" */
|
||||
|
||||
/* target "default" has 6 children: 4 src + 2 feature refs */
|
||||
for (n = doc->nodes; n != NULL && top_count-- > 0; n = n->next) {
|
||||
if (n->name.kind == ST_TOK_IDENT && n->name.len == 6 &&
|
||||
memcmp(n->name.text, "target", 6) == 0) {
|
||||
struct st_kdl_node *c;
|
||||
for (c = n->children; c != NULL; c = c->next) {
|
||||
target_children++;
|
||||
}
|
||||
}
|
||||
}
|
||||
munit_assert_size(target_children, ==, 6);
|
||||
|
||||
/* option "debug" default=#false: key and value spans */
|
||||
n = doc->nodes->next->next->next->next;
|
||||
munit_assert_size(n->name.span.line, ==, 43);
|
||||
munit_assert_size(n->args->value.span.col, ==, 8); /* "debug" */
|
||||
munit_assert_not_null(n->props);
|
||||
munit_assert_size(n->props->key.span.col, ==, 16); /* default */
|
||||
munit_assert_size(n->props->value.span.col, ==, 24); /* #false */
|
||||
|
||||
st_kdl_document_free(doc);
|
||||
free(src);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* (c) missing project: an empty document (and a NULL document) yields a
|
||||
* schema error with NO span (no file name is available for an empty
|
||||
* document). */
|
||||
static MunitResult
|
||||
test_missing_project(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
struct st_error *err = NULL;
|
||||
struct st_kdl_document *doc;
|
||||
|
||||
doc = st_kdl_parse("", "t.kdl", &err);
|
||||
munit_assert_not_null(doc);
|
||||
munit_assert_null(err);
|
||||
err = st_kdl_validate(doc);
|
||||
munit_assert_not_null(err);
|
||||
munit_assert_int(st_error_category_of(err), ==, ST_ERR_KDL_SCHEMA);
|
||||
munit_assert_null(err->span);
|
||||
munit_assert_true(
|
||||
strstr(st_error_message(err), "missing required 'project'") != NULL);
|
||||
st_error_free(err);
|
||||
st_kdl_document_free(doc);
|
||||
|
||||
/* comment-only documents are equally empty */
|
||||
doc = st_kdl_parse("// nothing\n", "t.kdl", &err);
|
||||
munit_assert_not_null(doc);
|
||||
err = st_kdl_validate(doc);
|
||||
munit_assert_not_null(err);
|
||||
munit_assert_null(err->span);
|
||||
st_error_free(err);
|
||||
st_kdl_document_free(doc);
|
||||
|
||||
/* a NULL document is the same failure */
|
||||
err = st_kdl_validate(NULL);
|
||||
munit_assert_not_null(err);
|
||||
munit_assert_int(st_error_category_of(err), ==, ST_ERR_KDL_SCHEMA);
|
||||
st_error_free(err);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* (c/d) project must be FIRST: a document starting with a non-project
|
||||
* node errors naming that node with its span. */
|
||||
static MunitResult
|
||||
test_project_not_first(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
assert_schema_error("target \"x\" { src \"a.c\" }\n"
|
||||
"project \"p\" version \"1.0\"",
|
||||
"'target'", 1, 1);
|
||||
assert_schema_error("feature \"f\" { header \"h.h\" }\n"
|
||||
"project \"p\" version \"1.0\"",
|
||||
"'feature'", 1, 1);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* (b) unknown top-level node names the node + span, in any position after
|
||||
* the project. */
|
||||
static MunitResult
|
||||
test_unknown_top_level(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
assert_schema_error("project \"p\" version \"1.0\"\n"
|
||||
"bogus \"x\"\n",
|
||||
"'bogus'", 2, 1);
|
||||
assert_schema_error("project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"wat 1\n",
|
||||
"'wat'", 3, 1);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* duplicate project nodes are rejected with the second project's span. */
|
||||
static MunitResult
|
||||
test_duplicate_project(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
assert_schema_error("project \"p\" version \"1.0\"\n"
|
||||
"project \"q\" version \"2.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n",
|
||||
"duplicate 'project'", 2, 1);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* (e) missing/extra required arguments on every node shape. */
|
||||
static MunitResult
|
||||
test_missing_args(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
/* project argument shapes */
|
||||
assert_schema_error("project", "requires a name argument", 1, 1);
|
||||
assert_schema_error("project \"p\"", "requires a version", 1, 1);
|
||||
assert_schema_error("project \"p\" version",
|
||||
"requires the version string", 1, 13);
|
||||
assert_schema_error("project \"p\" version \"1.0\" extra",
|
||||
"exactly three arguments", 1, 27);
|
||||
|
||||
/* target/feature/option names */
|
||||
assert_schema_error("project \"p\" version \"1.0\"\ntarget",
|
||||
"target requires a name argument", 2, 1);
|
||||
assert_schema_error("project \"p\" version \"1.0\"\ntarget \"t\" \"u\"",
|
||||
"takes exactly one argument", 2, 12);
|
||||
assert_schema_error("project \"p\" version \"1.0\"\nfeature",
|
||||
"feature requires a name argument", 2, 1);
|
||||
assert_schema_error("project \"p\" version \"1.0\"\noption",
|
||||
"option requires a name argument", 2, 1);
|
||||
|
||||
/* project-only: a build file needs at least one target or feature */
|
||||
assert_schema_error("project \"p\" version \"1.0\"",
|
||||
"missing 'target' or 'feature'", 1, 1);
|
||||
assert_schema_error("project \"p\" version \"1.0\"\noption \"x\"",
|
||||
"missing 'target' or 'feature'", 1, 1);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* wrong argument types are rejected via the value model, naming what was
|
||||
* expected and what was found. */
|
||||
static MunitResult
|
||||
test_wrong_arg_types(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
assert_schema_error("project 42 version \"1.0\"",
|
||||
"project name must be a non-empty string (got int)",
|
||||
1, 9);
|
||||
assert_schema_error("project \"p\" 99 \"1.0\"",
|
||||
"expected the keyword 'version'", 1, 13);
|
||||
assert_schema_error("project \"p\" version 42",
|
||||
"version must be a non-empty string (got int)", 1, 21);
|
||||
assert_schema_error("project \"p\" version \"\"",
|
||||
"version must be a non-empty string", 1, 21);
|
||||
assert_schema_error("project \"p\" version \"1.0\"\ntarget 42",
|
||||
"target name must be a non-empty string (got int)",
|
||||
2, 8);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* project extras: properties and children are not part of the pinned
|
||||
* grammar. */
|
||||
static MunitResult
|
||||
test_project_extras(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
assert_schema_error("project \"p\" version \"1.0\" foo=1",
|
||||
"unexpected property 'foo'", 1, 27);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\" { target \"t\" { src \"a.c\" } }",
|
||||
"takes no children", 1, 29);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* (g) unknown target children name the child + span; known children get
|
||||
* their argument/property/children shapes enforced. */
|
||||
static MunitResult
|
||||
test_target_children(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { cc-flags \"-O2\" }",
|
||||
"'cc-flags'", 2, 14);
|
||||
assert_schema_error("project \"p\" version \"1.0\"\ntarget \"t\" { jobs 8 }",
|
||||
"'jobs'", 2, 14);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { src }",
|
||||
"'src' requires a file argument", 2, 14);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { src 42 }",
|
||||
"'src' file must be a non-empty string (got int)", 2, 18);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { src \"a.c\" extra }",
|
||||
"'src' takes exactly one argument", 2, 24);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { src \"a.c\" foo=1 }",
|
||||
"'src' takes no properties", 2, 24);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { src \"a.c\" { x } }",
|
||||
"'src' takes no children", 2, 26);
|
||||
|
||||
/* feature references inside a target */
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { feature }",
|
||||
"'feature' requires a name argument", 2, 14);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { feature 42 }",
|
||||
"'feature' name must be a non-empty string (got int)", 2, 22);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\ntarget \"t\" { feature \"p\" foo=1 }",
|
||||
"'feature' takes no properties", 2, 26);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* feature children: structural validation only (the 8 check kinds are
|
||||
* todo 10). Any check name passes provided it has >= 1 non-empty string
|
||||
* argument and no children; check-kind semantics are out of scope here. */
|
||||
static MunitResult
|
||||
test_feature_checks_structural(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
/* the two fixture kinds plus arbitrary names all pass structurally */
|
||||
assert_valid("project \"p\" version \"1.0\"\n"
|
||||
"feature \"f\" { header \"h.h\"\nlibrary \"l\" }");
|
||||
assert_valid("project \"p\" version \"1.0\"\n"
|
||||
"feature \"f\" { type \"size_t\"\nsizeof \"int\"\n"
|
||||
"function \"strdup\"\nprogram \"gcc\"\n"
|
||||
"compiler_flag \"-Wall\"\npkg_config \"zlib\" }");
|
||||
assert_valid("project \"p\" version \"1.0\"\n"
|
||||
"feature \"f\" { frobnicate \"x\" }");
|
||||
|
||||
/* but a check still needs its argument (a non-empty string) */
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\nfeature \"f\" { header }",
|
||||
"check 'header' requires an argument", 2, 15);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\nfeature \"f\" { header 42 }",
|
||||
"check 'header' argument must be a non-empty string (got int)",
|
||||
2, 22);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\nfeature \"f\" { header \"h.h\" { x } }",
|
||||
"check 'header' takes no children", 2, 30);
|
||||
|
||||
/* feature shapes */
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\nfeature \"f\" \"g\" { header \"h.h\" }",
|
||||
"feature 'f' takes exactly one argument", 2, 13);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\nfeature \"f\" foo=1 { header \"h.h\" }",
|
||||
"feature 'f' has unexpected property 'foo'", 2, 13);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
/* (f) option: `default` is an optional bool property; non-bool defaults
|
||||
* are rejected with the value's span, as are other properties, duplicate
|
||||
* defaults, children, and annotated values. */
|
||||
static MunitResult
|
||||
test_option_default(const MunitParameter params[], void *data)
|
||||
{
|
||||
(void)params;
|
||||
(void)data;
|
||||
|
||||
assert_valid("project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"debug\" default=#true");
|
||||
assert_valid("project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"x\"");
|
||||
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"x\" default=\"yes\"",
|
||||
"'default' must be a boolean (got string)", 3, 20);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"x\" default=1",
|
||||
"'default' must be a boolean (got int)", 3, 20);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"x\" default=#null",
|
||||
"'default' must be a boolean (got null)", 3, 20);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"x\" default=(bool)#true",
|
||||
"'default' must not carry a type annotation", 3, 26);
|
||||
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"x\" foo=1",
|
||||
"unexpected property 'foo' (only 'default' is allowed)", 3, 12);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"x\" default=#true default=#false",
|
||||
"duplicate 'default' property", 3, 26);
|
||||
assert_schema_error(
|
||||
"project \"p\" version \"1.0\"\n"
|
||||
"target \"t\" { src \"a.c\" }\n"
|
||||
"option \"x\" { default #true }",
|
||||
"option 'x' takes no children", 3, 14);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
static MunitTest tests[] = {
|
||||
{ "/schema/valid-fixture", test_valid_fixture, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/missing-project", test_missing_project, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/project-not-first", test_project_not_first, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/unknown-top-level", test_unknown_top_level, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/duplicate-project", test_duplicate_project, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/missing-args", test_missing_args, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/wrong-arg-types", test_wrong_arg_types, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/project-extras", test_project_extras, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/target-children", test_target_children, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/feature-checks-structural", test_feature_checks_structural,
|
||||
NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ "/schema/option-default", test_option_default, NULL, NULL,
|
||||
MUNIT_TEST_OPTION_NONE, NULL },
|
||||
{ NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||
};
|
||||
|
||||
static const MunitSuite suite = {
|
||||
"/schema", 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);
|
||||
}
|
||||
Reference in New Issue
Block a user