mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-10-20 05:49:37 +08:00
Nine findings from the 2026-09-16 graph-quality probe over the eleven bench corpora, each with a
test that fails without its fix and a before/after measurement on the real corpus.
Deterministic resolution (O9). Two indexes of one kernel tree differed in 5,821 IMPORTS and 632
CALLS edges. Three order/load dependencies carried it: the `#include` target was the first by-name
hit whose path ends with the include path — a bucket in worker merge order (include/linux/device.h
vs tools/virtio/linux/device.h); the registry broke suffix_match score ties by bucket position; the
LSP budget-share rule measured wall time. Include targets now prefer the File node, the least nested
path, then the lexicographic path; registry ties prefer the least nested QN, then the smaller QN; the
budget share is measured in thread CPU time like the budget itself. Two kernel indexes are now
identical (8,529,723 nodes, 1,974,940 CALLS, zero churn), as are two elasticsearch indexes.
Findability. search_graph's BM25 path honours the `label` filter and ranks an exact-name definition
above test methods that repeat the token (15/15 probe queries).
Coverage. The parse-coverage gap tree subtracts definitions the walker recovered inside an error
region (multi-line only); kernel/sched/core.c reports 476 flagged lines instead of 7,674.
Precision. Config-language callers (Makefile, CMake, YAML) no longer bind unique_name into code
(kernel Makefile -> C CALLS 195 -> 0). JS-family token names ({}, 1, (x: T) => U) are not
definitions; computed, quoted and escaped member keys stay (TypeScript 204 junk -> 0, 1,782 real
names kept). Names are cut at their first line (elasticsearch YAML 8,306 -> 0); a comment inside an
argument list is not an argument, so no Route node is named by a Java block comment; the wildcard
route `/*` is still a route. HTML/Vue/Svelte/Astro embedded member calls stay unbound.
Recall. A Python call through a self- or cls-rooted attribute keeps a specific unique_name member
target (Django +751, elasticsearch +3,288 CALLS, no new callee names).
Robustness. Edge properties never carry a split UTF-8 sequence (the 120-byte expression cut and the
JSON escaper both trim a partial code point). CLI array flags accept a JSON-array literal. Cypher
seeds a single-hop pattern from its selective end (kernel `MATCH (a)-[:CALLS]->(b) WHERE b.name =
'_printk'`: execution limit -> 7.9 s).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
581 lines
15 KiB
C
581 lines
15 KiB
C
/*
|
|
* test_str_util.c — RED phase tests for foundation/str_util.
|
|
*/
|
|
#include "test_framework.h"
|
|
#include "../src/foundation/str_util.h"
|
|
|
|
static CBMArena a;
|
|
|
|
static void setup(void) {
|
|
cbm_arena_init(&a);
|
|
}
|
|
static void teardown(void) {
|
|
cbm_arena_destroy(&a);
|
|
}
|
|
|
|
TEST(path_join_basic) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join(&a, "src", "main.c"), "src/main.c");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_trailing_slash) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join(&a, "src/", "main.c"), "src/main.c");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_leading_slash) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join(&a, "src", "/main.c"), "src/main.c");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_empty_base) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join(&a, "", "main.c"), "main.c");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_empty_name) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join(&a, "src", ""), "src");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_n) {
|
|
setup();
|
|
const char *parts[] = {"a", "b", "c", "d.txt"};
|
|
ASSERT_STR_EQ(cbm_path_join_n(&a, parts, 4), "a/b/c/d.txt");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_ext) {
|
|
ASSERT_STR_EQ(cbm_path_ext("foo.go"), "go");
|
|
ASSERT_STR_EQ(cbm_path_ext("foo.tar.gz"), "gz");
|
|
ASSERT_STR_EQ(cbm_path_ext("Makefile"), "");
|
|
ASSERT_STR_EQ(cbm_path_ext(".gitignore"), "gitignore");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_base) {
|
|
ASSERT_STR_EQ(cbm_path_base("src/main.c"), "main.c");
|
|
ASSERT_STR_EQ(cbm_path_base("main.c"), "main.c");
|
|
ASSERT_STR_EQ(cbm_path_base("/a/b/c"), "c");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_dir) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_dir(&a, "src/main.c"), "src");
|
|
ASSERT_STR_EQ(cbm_path_dir(&a, "main.c"), ".");
|
|
ASSERT_STR_EQ(cbm_path_dir(&a, "/a/b/c"), "/a/b");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_starts_with) {
|
|
ASSERT_TRUE(cbm_str_starts_with("hello world", "hello"));
|
|
ASSERT_FALSE(cbm_str_starts_with("hello", "hello world"));
|
|
ASSERT_TRUE(cbm_str_starts_with("hello", ""));
|
|
ASSERT_TRUE(cbm_str_starts_with("hello", "hello"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_ends_with) {
|
|
ASSERT_TRUE(cbm_str_ends_with("hello world", "world"));
|
|
ASSERT_FALSE(cbm_str_ends_with("hello", "hello world"));
|
|
ASSERT_TRUE(cbm_str_ends_with("hello", ""));
|
|
ASSERT_TRUE(cbm_str_ends_with("hello", "hello"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_contains) {
|
|
ASSERT_TRUE(cbm_str_contains("hello world", "lo wo"));
|
|
ASSERT_FALSE(cbm_str_contains("hello", "xyz"));
|
|
ASSERT_TRUE(cbm_str_contains("hello", ""));
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_tolower) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_str_tolower(&a, "Hello World"), "hello world");
|
|
ASSERT_STR_EQ(cbm_str_tolower(&a, "already"), "already");
|
|
ASSERT_STR_EQ(cbm_str_tolower(&a, ""), "");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_replace_char) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_str_replace_char(&a, "a/b/c", '/', '.'), "a.b.c");
|
|
ASSERT_STR_EQ(cbm_str_replace_char(&a, "no-change", '/', '.'), "no-change");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_strip_ext) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_str_strip_ext(&a, "foo.go"), "foo");
|
|
ASSERT_STR_EQ(cbm_str_strip_ext(&a, "foo.tar.gz"), "foo.tar");
|
|
ASSERT_STR_EQ(cbm_str_strip_ext(&a, "Makefile"), "Makefile");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_split) {
|
|
setup();
|
|
int count = 0;
|
|
char **parts = cbm_str_split(&a, "a/b/c/d", '/', &count);
|
|
ASSERT_EQ(count, 4);
|
|
ASSERT_STR_EQ(parts[0], "a");
|
|
ASSERT_STR_EQ(parts[1], "b");
|
|
ASSERT_STR_EQ(parts[2], "c");
|
|
ASSERT_STR_EQ(parts[3], "d");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_split_empty) {
|
|
setup();
|
|
int count = 0;
|
|
char **parts = cbm_str_split(&a, "", '/', &count);
|
|
ASSERT_EQ(count, 1);
|
|
ASSERT_STR_EQ(parts[0], "");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_split_no_delim) {
|
|
setup();
|
|
int count = 0;
|
|
char **parts = cbm_str_split(&a, "hello", '/', &count);
|
|
ASSERT_EQ(count, 1);
|
|
ASSERT_STR_EQ(parts[0], "hello");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
/* ── NULL safety tests ─────────────────────────────────────────── */
|
|
|
|
TEST(path_join_null_base) {
|
|
setup();
|
|
ASSERT_NULL(cbm_path_join(&a, NULL, "main.c"));
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_null_name) {
|
|
setup();
|
|
ASSERT_NULL(cbm_path_join(&a, "src", NULL));
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_both_null) {
|
|
setup();
|
|
ASSERT_NULL(cbm_path_join(&a, NULL, NULL));
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_dir_null) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_dir(&a, NULL), ".");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_starts_with_null_s) {
|
|
ASSERT_FALSE(cbm_str_starts_with(NULL, "hello"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_starts_with_null_prefix) {
|
|
ASSERT_FALSE(cbm_str_starts_with("hello", NULL));
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_ends_with_null) {
|
|
ASSERT_FALSE(cbm_str_ends_with(NULL, "world"));
|
|
ASSERT_FALSE(cbm_str_ends_with("world", NULL));
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_contains_null) {
|
|
ASSERT_FALSE(cbm_str_contains(NULL, "sub"));
|
|
ASSERT_FALSE(cbm_str_contains("hello", NULL));
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_tolower_null) {
|
|
setup();
|
|
ASSERT_NULL(cbm_str_tolower(&a, NULL));
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_replace_char_null) {
|
|
setup();
|
|
ASSERT_NULL(cbm_str_replace_char(&a, NULL, 'a', 'b'));
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_strip_ext_null) {
|
|
setup();
|
|
ASSERT_NULL(cbm_str_strip_ext(&a, NULL));
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_split_null_string) {
|
|
setup();
|
|
int count = 0;
|
|
ASSERT_NULL(cbm_str_split(&a, NULL, '/', &count));
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(str_split_null_out_count) {
|
|
setup();
|
|
ASSERT_NULL(cbm_str_split(&a, "a/b", '/', NULL));
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
/* ── path_join edge cases ─────────────────────────────────────── */
|
|
|
|
TEST(path_join_multi_slashes) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join(&a, "src///", "///main.c"), "src/main.c");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_only_slash_base) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join(&a, "/", "main.c"), "main.c");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_long_paths) {
|
|
setup();
|
|
char long_base[512];
|
|
char long_name[512];
|
|
memset(long_base, 'a', 511);
|
|
long_base[511] = '\0';
|
|
memset(long_name, 'b', 511);
|
|
long_name[511] = '\0';
|
|
char *result = cbm_path_join(&a, long_base, long_name);
|
|
ASSERT_NOT_NULL(result);
|
|
ASSERT_TRUE(cbm_str_starts_with(result, "aaa"));
|
|
ASSERT_TRUE(cbm_str_ends_with(result, "bbb"));
|
|
ASSERT_TRUE(cbm_str_contains(result, "/"));
|
|
ASSERT_EQ(strlen(result), 511 + 1 + 511);
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_n_zero) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join_n(&a, NULL, 0), "");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_join_n_null_parts) {
|
|
setup();
|
|
ASSERT_STR_EQ(cbm_path_join_n(&a, NULL, 3), "");
|
|
teardown();
|
|
PASS();
|
|
}
|
|
|
|
/* ── path_ext edge cases ──────────────────────────────────────── */
|
|
|
|
TEST(path_ext_null) {
|
|
ASSERT_STR_EQ(cbm_path_ext(NULL), "");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_ext_dot_directory) {
|
|
ASSERT_STR_EQ(cbm_path_ext("dir.name/file"), "");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_ext_hidden_file) {
|
|
ASSERT_STR_EQ(cbm_path_ext(".gitignore"), "gitignore");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_ext_no_extension) {
|
|
ASSERT_STR_EQ(cbm_path_ext("Makefile"), "");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_ext_multiple_dots) {
|
|
ASSERT_STR_EQ(cbm_path_ext("file.test.spec.ts"), "ts");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_ext_trailing_dot) {
|
|
ASSERT_STR_EQ(cbm_path_ext("file."), "");
|
|
PASS();
|
|
}
|
|
|
|
/* ── path_base edge cases ─────────────────────────────────────── */
|
|
|
|
TEST(path_base_null) {
|
|
ASSERT_STR_EQ(cbm_path_base(NULL), "");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_base_empty) {
|
|
ASSERT_STR_EQ(cbm_path_base(""), "");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_base_just_filename) {
|
|
ASSERT_STR_EQ(cbm_path_base("hello.txt"), "hello.txt");
|
|
PASS();
|
|
}
|
|
|
|
TEST(path_base_trailing_slash) {
|
|
ASSERT_STR_EQ(cbm_path_base("dir/"), "");
|
|
PASS();
|
|
}
|
|
|
|
/* ── validate_shell_arg tests ─────────────────────────────────── */
|
|
|
|
TEST(validate_shell_arg_null) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg(NULL));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_safe) {
|
|
ASSERT_TRUE(cbm_validate_shell_arg("hello-world_123"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_single_quote) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg("it's bad"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_semicolon) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg("cmd; rm -rf"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_pipe) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg("cmd | evil"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_ampersand) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg("cmd & bg"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_dollar) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg("$HOME"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_backtick) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg("`whoami`"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_newline) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg("line1\nline2"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_carriage_return) {
|
|
ASSERT_FALSE(cbm_validate_shell_arg("line1\rline2"));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_backslash) {
|
|
#ifdef _WIN32
|
|
/* Backslash is allowed on Windows (path separator) */
|
|
ASSERT_TRUE(cbm_validate_shell_arg("path\\to\\file"));
|
|
#else
|
|
ASSERT_FALSE(cbm_validate_shell_arg("path\\to\\file"));
|
|
#endif
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_empty) {
|
|
ASSERT_TRUE(cbm_validate_shell_arg(""));
|
|
PASS();
|
|
}
|
|
|
|
TEST(validate_shell_arg_spaces) {
|
|
ASSERT_TRUE(cbm_validate_shell_arg("hello world with spaces"));
|
|
PASS();
|
|
}
|
|
|
|
/* ── JSON Escaping tests ──────────────────────────────────────── */
|
|
|
|
TEST(json_escape_control_chars) {
|
|
char buf[64];
|
|
const char *input = "A\x01"
|
|
"B\n";
|
|
int len = cbm_json_escape(buf, sizeof(buf), input);
|
|
ASSERT_STR_EQ(buf, "A\\u0001B\\n");
|
|
ASSERT_EQ(len, 10);
|
|
PASS();
|
|
}
|
|
|
|
/* A truncated escape must never end inside a multibyte UTF-8 sequence: the
|
|
* callee text of a CALLS edge is escaped into a fixed buffer, and a cut umlaut
|
|
* persisted bytes SQLite could not decode as text (2026-09-16 probe: rust,
|
|
* java and typescript stores). "aé" is a, 0xC3, 0xA9. */
|
|
TEST(json_escape_never_splits_utf8) {
|
|
char buf[3]; /* room for two payload bytes + NUL: the lead byte would fit, its continuation not
|
|
*/
|
|
int len = cbm_json_escape(buf, sizeof(buf), "a\xC3\xA9");
|
|
ASSERT_STR_EQ(buf, "a");
|
|
ASSERT_EQ(len, 1);
|
|
|
|
char whole[4];
|
|
len = cbm_json_escape(whole, sizeof(whole), "a\xC3\xA9");
|
|
ASSERT_STR_EQ(whole, "a\xC3\xA9");
|
|
ASSERT_EQ(len, 3);
|
|
|
|
/* A four-byte sequence cut after two bytes is dropped whole. */
|
|
char emoji[3];
|
|
len = cbm_json_escape(emoji, sizeof(emoji), "\xF0\x9F\x98\x80z");
|
|
ASSERT_STR_EQ(emoji, "");
|
|
ASSERT_EQ(len, 0);
|
|
PASS();
|
|
}
|
|
|
|
/* ── SNPRINTF_APPEND tests ────────────────────────────────────── */
|
|
|
|
TEST(snprintf_append_basic) {
|
|
char buf[64];
|
|
int off = 0;
|
|
CBM_SNPRINTF_APPEND(buf, sizeof(buf), off, "hello");
|
|
CBM_SNPRINTF_APPEND(buf, sizeof(buf), off, " world");
|
|
ASSERT_STR_EQ(buf, "hello world");
|
|
ASSERT_EQ(off, 11);
|
|
PASS();
|
|
}
|
|
|
|
TEST(snprintf_append_fills_exactly) {
|
|
char buf[6];
|
|
int off = 0;
|
|
CBM_SNPRINTF_APPEND(buf, sizeof(buf), off, "hello");
|
|
ASSERT_STR_EQ(buf, "hello");
|
|
ASSERT_EQ(off, 5);
|
|
PASS();
|
|
}
|
|
|
|
TEST(snprintf_append_overflow) {
|
|
char buf[8];
|
|
int off = 0;
|
|
CBM_SNPRINTF_APPEND(buf, sizeof(buf), off, "hello world this is way too long");
|
|
/* offset clamped to sizeof(buf) - 1 */
|
|
ASSERT_EQ(off, 7);
|
|
/* buffer is null-terminated and truncated */
|
|
ASSERT_EQ(strlen(buf), 7);
|
|
PASS();
|
|
}
|
|
|
|
TEST(snprintf_append_multiple_sequential) {
|
|
char buf[32];
|
|
int off = 0;
|
|
CBM_SNPRINTF_APPEND(buf, sizeof(buf), off, "a=%d", 1);
|
|
CBM_SNPRINTF_APPEND(buf, sizeof(buf), off, " b=%d", 2);
|
|
CBM_SNPRINTF_APPEND(buf, sizeof(buf), off, " c=%d", 3);
|
|
CBM_SNPRINTF_APPEND(buf, sizeof(buf), off, " d=%d", 4);
|
|
ASSERT_STR_EQ(buf, "a=1 b=2 c=3 d=4");
|
|
ASSERT_EQ(off, 15);
|
|
PASS();
|
|
}
|
|
|
|
SUITE(str_util) {
|
|
RUN_TEST(path_join_basic);
|
|
RUN_TEST(path_join_trailing_slash);
|
|
RUN_TEST(path_join_leading_slash);
|
|
RUN_TEST(path_join_empty_base);
|
|
RUN_TEST(path_join_empty_name);
|
|
RUN_TEST(path_join_n);
|
|
RUN_TEST(path_ext);
|
|
RUN_TEST(path_base);
|
|
RUN_TEST(path_dir);
|
|
RUN_TEST(str_starts_with);
|
|
RUN_TEST(str_ends_with);
|
|
RUN_TEST(str_contains);
|
|
RUN_TEST(str_tolower);
|
|
RUN_TEST(str_replace_char);
|
|
RUN_TEST(str_strip_ext);
|
|
RUN_TEST(str_split);
|
|
RUN_TEST(str_split_empty);
|
|
RUN_TEST(str_split_no_delim);
|
|
/* NULL safety */
|
|
RUN_TEST(path_join_null_base);
|
|
RUN_TEST(path_join_null_name);
|
|
RUN_TEST(path_join_both_null);
|
|
RUN_TEST(path_dir_null);
|
|
RUN_TEST(str_starts_with_null_s);
|
|
RUN_TEST(str_starts_with_null_prefix);
|
|
RUN_TEST(str_ends_with_null);
|
|
RUN_TEST(str_contains_null);
|
|
RUN_TEST(str_tolower_null);
|
|
RUN_TEST(str_replace_char_null);
|
|
RUN_TEST(str_strip_ext_null);
|
|
RUN_TEST(str_split_null_string);
|
|
RUN_TEST(str_split_null_out_count);
|
|
/* path_join edge cases */
|
|
RUN_TEST(path_join_multi_slashes);
|
|
RUN_TEST(path_join_only_slash_base);
|
|
RUN_TEST(path_join_long_paths);
|
|
RUN_TEST(path_join_n_zero);
|
|
RUN_TEST(path_join_n_null_parts);
|
|
/* path_ext edge cases */
|
|
RUN_TEST(path_ext_null);
|
|
RUN_TEST(path_ext_dot_directory);
|
|
RUN_TEST(path_ext_hidden_file);
|
|
RUN_TEST(path_ext_no_extension);
|
|
RUN_TEST(path_ext_multiple_dots);
|
|
RUN_TEST(path_ext_trailing_dot);
|
|
/* path_base edge cases */
|
|
RUN_TEST(path_base_null);
|
|
RUN_TEST(path_base_empty);
|
|
RUN_TEST(path_base_just_filename);
|
|
RUN_TEST(path_base_trailing_slash);
|
|
/* validate_shell_arg */
|
|
RUN_TEST(validate_shell_arg_null);
|
|
RUN_TEST(validate_shell_arg_safe);
|
|
RUN_TEST(validate_shell_arg_single_quote);
|
|
RUN_TEST(validate_shell_arg_semicolon);
|
|
RUN_TEST(validate_shell_arg_pipe);
|
|
RUN_TEST(validate_shell_arg_ampersand);
|
|
RUN_TEST(validate_shell_arg_dollar);
|
|
RUN_TEST(validate_shell_arg_backtick);
|
|
RUN_TEST(validate_shell_arg_newline);
|
|
RUN_TEST(validate_shell_arg_carriage_return);
|
|
RUN_TEST(validate_shell_arg_backslash);
|
|
RUN_TEST(validate_shell_arg_empty);
|
|
RUN_TEST(validate_shell_arg_spaces);
|
|
/* JSON Escaping */
|
|
RUN_TEST(json_escape_control_chars);
|
|
RUN_TEST(json_escape_never_splits_utf8);
|
|
/* SNPRINTF_APPEND */
|
|
RUN_TEST(snprintf_append_basic);
|
|
RUN_TEST(snprintf_append_fills_exactly);
|
|
RUN_TEST(snprintf_append_overflow);
|
|
RUN_TEST(snprintf_append_multiple_sequential);
|
|
}
|