1147 lines
41 KiB
C
1147 lines
41 KiB
C
/*
|
|
* test_grammar_probe_a.c — Grammar-only node/import/inheritance probe.
|
|
*
|
|
* SCOPE
|
|
* ─────
|
|
* Probes 12 grammar-only languages: ada, awk, cairo, clojure, commonlisp,
|
|
* crystal, d, emacslisp, fennel, fish, fsharp, gleam.
|
|
*
|
|
* CALLS is already covered for these languages by the P5 contract_calls_breadth
|
|
* suite in test_lang_contract.c (CALL_CASES rows). This file probes the OTHER
|
|
* dimensions that breadth doesn't cover:
|
|
* • NODE creation — definition-kind diversity (functions, classes, structs,
|
|
* records, constants, modules, protocols, etc.)
|
|
* • IMPORTS edges — the language's cross-file import/require/use/open
|
|
* mechanism at the graph layer (two-file fixture).
|
|
* • INHERITANCE — OOP languages: Crystal `<`, D `:`, F# `inherit`,
|
|
* Ada tagged-type extension.
|
|
* • Other — language-specific constructs (D structs/interfaces,
|
|
* F# records, Clojure protocols/defrecord, etc.)
|
|
*
|
|
* COLOUR LEGEND
|
|
* ─────────────
|
|
* GREEN = guard: the pipeline already produces the correct result; a failure
|
|
* here is a real regression.
|
|
* RED = bug reproduction: the pipeline does NOT yet produce the expected
|
|
* node/edge; the test FAILS until the bug is fixed. Brief inline
|
|
* comments document the root-cause class.
|
|
*
|
|
* CALLS dimensions are intentionally omitted here (covered by P5 breadth).
|
|
* Do NOT register this suite in test_main.c — a sibling agent owns that file.
|
|
*
|
|
* SUITE(grammar_probe_a)
|
|
*/
|
|
|
|
#include "../src/foundation/compat.h"
|
|
#include "test_framework.h"
|
|
#include "test_helpers.h"
|
|
#include "cbm.h"
|
|
#include <mcp/mcp.h>
|
|
#include <store/store.h>
|
|
#include <pipeline/pipeline.h>
|
|
#include <foundation/log.h>
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* Harness — mirrors test_lang_contract.c / test_node_creation_probe.c.
|
|
* Prefix "gpa_" to avoid symbol collisions with sibling probe files.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
typedef struct {
|
|
char tmpdir[256];
|
|
char dbpath[512];
|
|
char *project;
|
|
cbm_mcp_server_t *srv;
|
|
} GpaProj;
|
|
|
|
typedef struct {
|
|
const char *name; /* relative filename, may include '/' for subdirs */
|
|
const char *content;
|
|
} GpaFile;
|
|
|
|
static void gpa_to_fwd_slashes(char *p) {
|
|
for (; *p; p++) {
|
|
if (*p == '\\') *p = '/';
|
|
}
|
|
}
|
|
|
|
static cbm_store_t *gpa_open_indexed(GpaProj *lp) {
|
|
lp->project = cbm_project_name_from_path(lp->tmpdir);
|
|
if (!lp->project) return NULL;
|
|
const char *home = getenv("HOME");
|
|
if (!home) home = "/tmp";
|
|
char cache_dir[512];
|
|
snprintf(cache_dir, sizeof(cache_dir), "%s/.cache/codebase-memory-mcp", home);
|
|
cbm_mkdir(cache_dir);
|
|
snprintf(lp->dbpath, sizeof(lp->dbpath), "%s/%s.db", cache_dir, lp->project);
|
|
unlink(lp->dbpath);
|
|
lp->srv = cbm_mcp_server_new(NULL);
|
|
if (!lp->srv) return NULL;
|
|
char args[700];
|
|
snprintf(args, sizeof(args), "{\"repo_path\":\"%s\"}", lp->tmpdir);
|
|
char *resp = cbm_mcp_handle_tool(lp->srv, "index_repository", args);
|
|
if (resp) free(resp);
|
|
return cbm_store_open_path(lp->dbpath);
|
|
}
|
|
|
|
static cbm_store_t *gpa_index_files(GpaProj *lp, const GpaFile *files, int nfiles) {
|
|
memset(lp, 0, sizeof(*lp));
|
|
snprintf(lp->tmpdir, sizeof(lp->tmpdir), "/tmp/cbm_gpa_XXXXXX");
|
|
if (!cbm_mkdtemp(lp->tmpdir)) return NULL;
|
|
gpa_to_fwd_slashes(lp->tmpdir);
|
|
for (int i = 0; i < nfiles; i++) {
|
|
char path[700];
|
|
snprintf(path, sizeof(path), "%s/%s", lp->tmpdir, files[i].name);
|
|
char *slash = strrchr(path, '/');
|
|
if (slash && slash > path + strlen(lp->tmpdir)) {
|
|
*slash = '\0';
|
|
cbm_mkdir_p(path, 0755);
|
|
*slash = '/';
|
|
}
|
|
FILE *f = fopen(path, "wb");
|
|
if (!f) return NULL;
|
|
fputs(files[i].content, f);
|
|
fclose(f);
|
|
}
|
|
return gpa_open_indexed(lp);
|
|
}
|
|
|
|
static void gpa_cleanup(GpaProj *lp, cbm_store_t *store) {
|
|
if (store) cbm_store_close(store);
|
|
if (lp->srv) { cbm_mcp_server_free(lp->srv); lp->srv = NULL; }
|
|
free(lp->project);
|
|
lp->project = NULL;
|
|
th_rmtree(lp->tmpdir);
|
|
unlink(lp->dbpath);
|
|
char wal[600], shm[600];
|
|
snprintf(wal, sizeof(wal), "%s-wal", lp->dbpath);
|
|
unlink(wal);
|
|
snprintf(shm, sizeof(shm), "%s-shm", lp->dbpath);
|
|
unlink(shm);
|
|
}
|
|
|
|
/* ── Node-count helpers ─────────────────────────────────────────── */
|
|
|
|
static int gpa_count_label(cbm_store_t *store, const char *project, const char *label) {
|
|
cbm_node_t *nodes = NULL;
|
|
int count = 0;
|
|
if (cbm_store_find_nodes_by_label(store, project, label, &nodes, &count) != CBM_STORE_OK)
|
|
return -1;
|
|
cbm_store_free_nodes(nodes, count);
|
|
return count;
|
|
}
|
|
|
|
/* Sum of all type-like labels. */
|
|
static int gpa_type_nodes(cbm_store_t *store, const char *project) {
|
|
static const char *labels[] = {"Class","Struct","Interface","Enum","Trait","Type",NULL};
|
|
int total = 0;
|
|
for (int i = 0; labels[i]; i++) {
|
|
int n = gpa_count_label(store, project, labels[i]);
|
|
if (n > 0) total += n;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
/* Metrics bundled per index pass. */
|
|
typedef struct {
|
|
int ok;
|
|
int total_nodes;
|
|
int functions;
|
|
int methods;
|
|
int types; /* type-like sum */
|
|
int imports; /* IMPORTS edges */
|
|
int inherits; /* INHERITS edges */
|
|
} GpaMetrics;
|
|
|
|
static GpaMetrics gpa_metrics_files(const GpaFile *files, int nfiles) {
|
|
GpaProj lp;
|
|
cbm_store_t *store = gpa_index_files(&lp, files, nfiles);
|
|
GpaMetrics m = {0};
|
|
if (store) {
|
|
m.ok = 1;
|
|
m.total_nodes = cbm_store_count_nodes(store, lp.project);
|
|
m.functions = gpa_count_label(store, lp.project, "Function");
|
|
m.methods = gpa_count_label(store, lp.project, "Method");
|
|
m.types = gpa_type_nodes(store, lp.project);
|
|
m.imports = cbm_store_count_edges_by_type(store, lp.project, "IMPORTS");
|
|
m.inherits = cbm_store_count_edges_by_type(store, lp.project, "INHERITS");
|
|
}
|
|
gpa_cleanup(&lp, store);
|
|
return m;
|
|
}
|
|
|
|
static GpaMetrics gpa_metrics(const char *filename, const char *content) {
|
|
GpaFile f = {filename, content};
|
|
return gpa_metrics_files(&f, 1);
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 1 — Ada (.adb)
|
|
*
|
|
* Ada label histogram (from test_grammar_labels.c): Function:1, Module:1
|
|
* OOP: Ada 95 tagged types can model inheritance but grammar-only extraction
|
|
* produces Function/Module — no Class node expected.
|
|
* IMPORTS: Ada `with Pkg;` imports are extracted at the source level.
|
|
* Graph-level IMPORTS edges require the package body to be present;
|
|
* single-file fixture is expected RED (resolver can't find the target).
|
|
* CALLS: covered by P5 breadth (CALL_CASES ada entry); not probed here.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Ada: two procedure definitions → at least 2 Function nodes. */
|
|
TEST(probe_ada_functions) {
|
|
GpaMetrics m = gpa_metrics("calc.adb",
|
|
"procedure Calc is\n"
|
|
" function Add(A, B : Integer) return Integer is\n"
|
|
" begin\n"
|
|
" return A + B;\n"
|
|
" end Add;\n"
|
|
" function Sub(A, B : Integer) return Integer is\n"
|
|
" begin\n"
|
|
" return A - B;\n"
|
|
" end Sub;\n"
|
|
"begin\n"
|
|
" null;\n"
|
|
"end Calc;\n");
|
|
ASSERT_TRUE(m.ok);
|
|
ASSERT_TRUE(m.functions >= 1); /* GREEN: at least the outer procedure or inner ones */
|
|
PASS();
|
|
}
|
|
|
|
/* Ada: a package spec (.ads) with multiple subprogram specs → Function nodes. */
|
|
TEST(probe_ada_package_spec_nodes) {
|
|
GpaMetrics m = gpa_metrics("math.ads",
|
|
"package Math is\n"
|
|
" function Square(X : Float) return Float;\n"
|
|
" function Cube(X : Float) return Float;\n"
|
|
" function Abs_Val(X : Float) return Float;\n"
|
|
"end Math;\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: spec subprogram declarations must produce Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* Ada: pipeline indexes the file and produces at least 1 node (no crash). */
|
|
TEST(probe_ada_no_crash) {
|
|
GpaMetrics m = gpa_metrics("hello.adb",
|
|
"with Ada.Text_IO;\n"
|
|
"procedure Hello is\n"
|
|
"begin\n"
|
|
" Ada.Text_IO.Put_Line(\"Hello\");\n"
|
|
"end Hello;\n");
|
|
ASSERT_TRUE(m.ok);
|
|
ASSERT_TRUE(m.total_nodes >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* Ada: `with` import in a two-file fixture → IMPORTS edge at graph level.
|
|
* RED: grammar-only Ada has no import-resolution pass that turns `with Pkg`
|
|
* into a graph IMPORTS edge pointing to the package body node. */
|
|
TEST(probe_ada_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"math.ads",
|
|
"package Math is\n"
|
|
" function Square(X : Float) return Float;\n"
|
|
"end Math;\n"},
|
|
{"main.adb",
|
|
"with Math;\n"
|
|
"procedure Main is\n"
|
|
" R : Float;\n"
|
|
"begin\n"
|
|
" R := Math.Square(3.0);\n"
|
|
"end Main;\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: Ada `with` not yet resolved into IMPORTS edges by the pipeline. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED — no Ada import resolver */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 2 — Awk (.awk)
|
|
*
|
|
* Awk label histogram: Function:2, Module:1
|
|
* Awk has no imports, no OOP, no types — only user-defined functions.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Awk: two function definitions → 2 Function nodes. */
|
|
TEST(probe_awk_functions) {
|
|
GpaMetrics m = gpa_metrics("stats.awk",
|
|
"function max(a, b) {\n"
|
|
" return (a > b) ? a : b\n"
|
|
"}\n"
|
|
"\n"
|
|
"function min(a, b) {\n"
|
|
" return (a < b) ? a : b\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both user-defined functions must reach the graph. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Awk: three functions in one file — broader coverage of def-count floor. */
|
|
TEST(probe_awk_three_functions) {
|
|
GpaMetrics m = gpa_metrics("util.awk",
|
|
"function trim(s) { sub(/^[ \\t]+/, \"\", s); sub(/[ \\t]+$/, \"\", s); return s }\n"
|
|
"function upper(s, i, c, r) { for(i=1;i<=length(s);i++) r=r toupper(substr(s,i,1)); return r }\n"
|
|
"function repeat(s, n, i, r) { for(i=0;i<n;i++) r=r s; return r }\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: all three functions must appear as nodes. */
|
|
ASSERT_TRUE(m.functions >= 3);
|
|
PASS();
|
|
}
|
|
|
|
/* Awk has no OOP — no type-like nodes expected. */
|
|
TEST(probe_awk_no_type_nodes) {
|
|
GpaMetrics m = gpa_metrics("proc.awk",
|
|
"function process(line) {\n"
|
|
" print line\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: awk produces 0 type-like nodes (no classes/structs). */
|
|
ASSERT_TRUE(m.types == 0);
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 3 — Cairo (.cairo)
|
|
*
|
|
* Cairo label histogram: Function:2, Module:1
|
|
* Cairo (Starknet) is a Rust-like language — free functions and `fn` items.
|
|
* No OOP classes; no cross-file import mechanism indexable in single pass.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Cairo: two free functions → 2 Function nodes. */
|
|
TEST(probe_cairo_functions) {
|
|
GpaMetrics m = gpa_metrics("math.cairo",
|
|
"fn add(x: felt252, y: felt252) -> felt252 {\n"
|
|
" x + y\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn mul(x: felt252, y: felt252) -> felt252 {\n"
|
|
" x * y\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both functions must be graph nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Cairo: trait definition — if the grammar/extractor models it. */
|
|
TEST(probe_cairo_trait_or_type) {
|
|
GpaMetrics m = gpa_metrics("iface.cairo",
|
|
"trait IMath {\n"
|
|
" fn add(x: felt252, y: felt252) -> felt252;\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn zero() -> felt252 { 0 }\n");
|
|
ASSERT_TRUE(m.ok);
|
|
ASSERT_TRUE(m.total_nodes >= 1); /* GREEN: at least the function node. */
|
|
PASS();
|
|
}
|
|
|
|
/* Cairo: three functions to exercise def-count floor. */
|
|
TEST(probe_cairo_three_functions) {
|
|
GpaMetrics m = gpa_metrics("ops.cairo",
|
|
"fn identity(x: felt252) -> felt252 { x }\n"
|
|
"fn negate(x: felt252) -> felt252 { -x }\n"
|
|
"fn square(x: felt252) -> felt252 { x * x }\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: all three free functions. */
|
|
ASSERT_TRUE(m.functions >= 3);
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 4 — Clojure (.clj)
|
|
*
|
|
* Clojure label histogram: Function:2, Module:1
|
|
* Clojure is a Lisp; no OOP classes. Namespace with `ns` + `:require`.
|
|
* IMPORTS: `ns` with `:require` can form cross-ns dependencies.
|
|
* CALLS: known gap (lisp list-head not resolved as callee).
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Clojure: two `defn` definitions → 2 Function nodes. */
|
|
TEST(probe_clojure_functions) {
|
|
GpaMetrics m = gpa_metrics("core.clj",
|
|
"(ns myapp.core)\n"
|
|
"\n"
|
|
"(defn greet [name]\n"
|
|
" (str \"Hello, \" name))\n"
|
|
"\n"
|
|
"(defn farewell [name]\n"
|
|
" (str \"Goodbye, \" name))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both defn forms must produce Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Clojure: defmulti / defmethod (polymorphic dispatch). */
|
|
TEST(probe_clojure_defmulti) {
|
|
GpaMetrics m = gpa_metrics("dispatch.clj",
|
|
"(ns myapp.dispatch)\n"
|
|
"\n"
|
|
"(defmulti area :shape)\n"
|
|
"\n"
|
|
"(defmethod area :circle [s]\n"
|
|
" (* Math/PI (:r s) (:r s)))\n"
|
|
"\n"
|
|
"(defmethod area :rect [s]\n"
|
|
" (* (:w s) (:h s)))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: at least the defmulti + defmethod forms produce Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* Clojure: defrecord → type-like node (if grammar models it). */
|
|
TEST(probe_clojure_defrecord) {
|
|
GpaMetrics m = gpa_metrics("model.clj",
|
|
"(ns myapp.model)\n"
|
|
"\n"
|
|
"(defrecord Point [x y])\n"
|
|
"\n"
|
|
"(defn make-point [x y] (->Point x y))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
ASSERT_TRUE(m.total_nodes >= 1); /* GREEN: at minimum the function node. */
|
|
PASS();
|
|
}
|
|
|
|
/* Clojure: two-file :require → IMPORTS edge.
|
|
* RED: grammar-only Clojure has no ns-resolution pass in the pipeline. */
|
|
TEST(probe_clojure_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"util.clj",
|
|
"(ns myapp.util)\n"
|
|
"(defn double-it [x] (* 2 x))\n"},
|
|
{"core.clj",
|
|
"(ns myapp.core\n"
|
|
" (:require [myapp.util :as u]))\n"
|
|
"\n"
|
|
"(defn run [n] (u/double-it n))\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: Clojure ns :require not resolved into IMPORTS edges. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 5 — Common Lisp (.lisp)
|
|
*
|
|
* CommonLisp label histogram: Function:2, Module:1
|
|
* Common Lisp uses `defun` for functions; `defpackage`/`in-package` for namespaces.
|
|
* No class hierarchy (CLOS exists but grammar-only extraction won't model it).
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Common Lisp: two `defun` definitions → 2 Function nodes. */
|
|
TEST(probe_commonlisp_functions) {
|
|
GpaMetrics m = gpa_metrics("math.lisp",
|
|
"(defun square (x)\n"
|
|
" (* x x))\n"
|
|
"\n"
|
|
"(defun cube (x)\n"
|
|
" (* x (square x)))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both defun forms must be Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Common Lisp: `defmacro` — if extractor models it as a Function. */
|
|
TEST(probe_commonlisp_defmacro) {
|
|
GpaMetrics m = gpa_metrics("macros.lisp",
|
|
"(defmacro when-positive (n &body body)\n"
|
|
" `(when (> ,n 0) ,@body))\n"
|
|
"\n"
|
|
"(defun use-macro (x)\n"
|
|
" (when-positive x\n"
|
|
" (format t \"positive~%\")))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: at minimum defun should produce a Function node. */
|
|
ASSERT_TRUE(m.functions >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* Common Lisp: defstruct → type-like node (if grammar models it). */
|
|
TEST(probe_commonlisp_defstruct) {
|
|
GpaMetrics m = gpa_metrics("types.lisp",
|
|
"(defstruct point\n"
|
|
" (x 0)\n"
|
|
" (y 0))\n"
|
|
"\n"
|
|
"(defun make-pt (a b)\n"
|
|
" (make-point :x a :y b))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
ASSERT_TRUE(m.total_nodes >= 1); /* GREEN: at minimum the function. */
|
|
PASS();
|
|
}
|
|
|
|
/* Common Lisp: `require` in two-file fixture → IMPORTS edge.
|
|
* RED: grammar-only CL has no package-resolution pass in the pipeline. */
|
|
TEST(probe_commonlisp_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"util.lisp",
|
|
"(defpackage :util (:export :double))\n"
|
|
"(in-package :util)\n"
|
|
"(defun double (x) (* 2 x))\n"},
|
|
{"main.lisp",
|
|
"(defpackage :main (:use :cl :util))\n"
|
|
"(in-package :main)\n"
|
|
"(defun run (n) (util:double n))\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: CL package :use not resolved into IMPORTS edges. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 6 — Crystal (.cr)
|
|
*
|
|
* Crystal label histogram: Class:1, Function:1, Module:1
|
|
* Crystal is an OOP language with Ruby-like syntax.
|
|
* Inheritance: `class Child < Parent` — extractor should populate base_classes.
|
|
* Imports: `require "./module"` — cross-file IMPORTS possible.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Crystal: class and method definitions → Class + Function/Method nodes. */
|
|
TEST(probe_crystal_class_node) {
|
|
GpaMetrics m = gpa_metrics("animal.cr",
|
|
"class Animal\n"
|
|
" def initialize(@name : String)\n"
|
|
" end\n"
|
|
"\n"
|
|
" def speak : String\n"
|
|
" \"...\"\n"
|
|
" end\n"
|
|
"end\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: class Animal must produce a Class node. */
|
|
ASSERT_TRUE(m.types >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* Crystal: class with inheritance `class Dog < Animal` → Class + INHERITS edge.
|
|
* GREEN for Class node (histogram confirms Class:1); INHERITS edge status probed
|
|
* separately below. */
|
|
TEST(probe_crystal_subclass_node) {
|
|
GpaMetrics m = gpa_metrics("pets.cr",
|
|
"class Animal\n"
|
|
" def breathe; end\n"
|
|
"end\n"
|
|
"\n"
|
|
"class Dog < Animal\n"
|
|
" def bark; \"woof\"; end\n"
|
|
"end\n"
|
|
"\n"
|
|
"class Cat < Animal\n"
|
|
" def meow; \"meow\"; end\n"
|
|
"end\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: at least 2 class nodes (Dog, Cat; Animal also expected). */
|
|
ASSERT_TRUE(m.types >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Crystal: INHERITS edge via `class Dog < Animal`.
|
|
* RED: grammar-only Crystal pipeline does not yet resolve base_classes into
|
|
* INHERITS graph edges (no Crystal branch in pass_semantic.c resolver). */
|
|
TEST(probe_crystal_inherits_edge) {
|
|
GpaMetrics m = gpa_metrics("inherit.cr",
|
|
"class Base\n"
|
|
" def do_base; end\n"
|
|
"end\n"
|
|
"\n"
|
|
"class Derived < Base\n"
|
|
" def do_derived; end\n"
|
|
"end\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: Crystal INHERITS edge not yet produced by the pipeline. */
|
|
ASSERT_TRUE(m.inherits >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* Crystal: module definition → type-like node. */
|
|
TEST(probe_crystal_module_node) {
|
|
GpaMetrics m = gpa_metrics("mixin.cr",
|
|
"module Greetable\n"
|
|
" def greet\n"
|
|
" puts \"Hello!\"\n"
|
|
" end\n"
|
|
"end\n"
|
|
"\n"
|
|
"class Person\n"
|
|
" include Greetable\n"
|
|
"end\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both the module and class should yield type-like nodes. */
|
|
ASSERT_TRUE(m.types >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* Crystal: require in two-file fixture → IMPORTS edge.
|
|
* RED: grammar-only Crystal has no `require` resolver in the pipeline. */
|
|
TEST(probe_crystal_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"util.cr",
|
|
"def double(x : Int32) : Int32\n"
|
|
" x * 2\n"
|
|
"end\n"},
|
|
{"main.cr",
|
|
"require \"./util\"\n"
|
|
"\n"
|
|
"def run(n : Int32) : Int32\n"
|
|
" double(n)\n"
|
|
"end\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: Crystal `require` not resolved into IMPORTS edges. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 7 — D (.d) — CBM_LANG_DLANG
|
|
*
|
|
* D label histogram: Function:2, Module:1
|
|
* D has `import`, structs, classes with `:` inheritance, interfaces.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* D: two free functions → 2 Function nodes. */
|
|
TEST(probe_d_functions) {
|
|
GpaMetrics m = gpa_metrics("math.d",
|
|
"int square(int x)\n"
|
|
"{\n"
|
|
" return x * x;\n"
|
|
"}\n"
|
|
"\n"
|
|
"int cube(int x)\n"
|
|
"{\n"
|
|
" return x * square(x);\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both functions must be Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* D: struct definition → Struct/type-like node.
|
|
* RED: the grammar-only D extractor produces Function:2/Module:1 (histogram);
|
|
* structs may not yet be extracted as distinct Struct nodes. */
|
|
TEST(probe_d_struct_node) {
|
|
GpaMetrics m = gpa_metrics("point.d",
|
|
"struct Point\n"
|
|
"{\n"
|
|
" double x;\n"
|
|
" double y;\n"
|
|
"}\n"
|
|
"\n"
|
|
"double dist(Point a, Point b)\n"
|
|
"{\n"
|
|
" import std.math : sqrt;\n"
|
|
" return sqrt((a.x-b.x)^^2 + (a.y-b.y)^^2);\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* REAL BUG (NEW-16, node-extraction incompleteness): d_class_types DOES list
|
|
* "struct_declaration" (lang_specs.c:943-950), so the spec is configured, yet 0
|
|
* type nodes reach the graph. D struct/class/interface defs are not being
|
|
* extracted despite the spec — root cause is in the D def-extraction path. */
|
|
ASSERT_TRUE(m.types >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* D: class with inheritance `class Dog : Animal`.
|
|
* RED: D OOP classes not expected in the current histogram (only Function/Module). */
|
|
TEST(probe_d_class_inherits) {
|
|
GpaMetrics m = gpa_metrics("pets.d",
|
|
"class Animal\n"
|
|
"{\n"
|
|
" void breathe() {}\n"
|
|
"}\n"
|
|
"\n"
|
|
"class Dog : Animal\n"
|
|
"{\n"
|
|
" void bark() {}\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: D class not yet extracted as Class node nor INHERITS edge produced. */
|
|
ASSERT_TRUE(m.types >= 1); /* expected RED — no D class extractor */
|
|
ASSERT_TRUE(m.inherits >= 1); /* expected RED — no D INHERITS resolver */
|
|
PASS();
|
|
}
|
|
|
|
/* D: interface definition.
|
|
* RED: D interfaces not expected in current grammar histogram. */
|
|
TEST(probe_d_interface_node) {
|
|
GpaMetrics m = gpa_metrics("iface.d",
|
|
"interface IWriter\n"
|
|
"{\n"
|
|
" void write(string s);\n"
|
|
" void flush();\n"
|
|
"}\n"
|
|
"\n"
|
|
"void noop() {}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: D interface not yet yielding Interface node. */
|
|
ASSERT_TRUE(m.types >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* D: import in two-file fixture → IMPORTS edge.
|
|
* RED: grammar-only D has no import resolver in the pipeline. */
|
|
TEST(probe_d_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"util.d",
|
|
"module util;\n"
|
|
"\n"
|
|
"int double_val(int x) { return x * 2; }\n"},
|
|
{"main.d",
|
|
"module main;\n"
|
|
"\n"
|
|
"import util;\n"
|
|
"\n"
|
|
"void run() {\n"
|
|
" int y = double_val(21);\n"
|
|
"}\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: D `import` not resolved into IMPORTS edges. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 8 — Emacs Lisp (.el)
|
|
*
|
|
* EmacsLisp label histogram: Function:2, Module:1
|
|
* Emacs Lisp uses `defun`; `require`/`provide` for modules; no OOP classes.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Emacs Lisp: two `defun` definitions → 2 Function nodes. */
|
|
TEST(probe_emacslisp_functions) {
|
|
GpaMetrics m = gpa_metrics("util.el",
|
|
"(defun el-square (x)\n"
|
|
" \"Return X squared.\"\n"
|
|
" (* x x))\n"
|
|
"\n"
|
|
"(defun el-cube (x)\n"
|
|
" \"Return X cubed.\"\n"
|
|
" (* x (el-square x)))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both defun forms must produce Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Emacs Lisp: defvar / defconst — global variable definitions. */
|
|
TEST(probe_emacslisp_defvar) {
|
|
GpaMetrics m = gpa_metrics("config.el",
|
|
"(defvar my-config-max 100\n"
|
|
" \"Maximum value for config.\")\n"
|
|
"\n"
|
|
"(defconst my-version \"1.0\"\n"
|
|
" \"Package version.\")\n"
|
|
"\n"
|
|
"(defun my-get-max () my-config-max)\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: at minimum the defun should produce a Function node. */
|
|
ASSERT_TRUE(m.functions >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* Emacs Lisp: `require` in two-file fixture → IMPORTS edge.
|
|
* RED: grammar-only EmacsLisp has no require-resolver in the pipeline. */
|
|
TEST(probe_emacslisp_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"math-util.el",
|
|
"(provide 'math-util)\n"
|
|
"\n"
|
|
"(defun math-util-double (x) (* 2 x))\n"},
|
|
{"main.el",
|
|
"(require 'math-util)\n"
|
|
"\n"
|
|
"(defun main-run (n) (math-util-double n))\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: EmacsLisp `require` not resolved into IMPORTS edges. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 9 — Fennel (.fnl)
|
|
*
|
|
* Fennel label histogram: Function:2, Module:1
|
|
* Fennel is a Lisp that compiles to Lua; `require` for imports; no OOP classes.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Fennel: two `fn` definitions → 2 Function nodes. */
|
|
TEST(probe_fennel_functions) {
|
|
GpaMetrics m = gpa_metrics("ops.fnl",
|
|
"(fn add [a b]\n"
|
|
" (+ a b))\n"
|
|
"\n"
|
|
"(fn mul [a b]\n"
|
|
" (* a b))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both fn forms must be Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Fennel: `lambda` (shorthand fn) definition. */
|
|
TEST(probe_fennel_lambda) {
|
|
GpaMetrics m = gpa_metrics("lambdas.fnl",
|
|
"(fn double [x] (* 2 x))\n"
|
|
"(fn square [x] (* x x))\n"
|
|
"(fn cube [x] (* x x x))\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: all three fn forms must be Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 3);
|
|
PASS();
|
|
}
|
|
|
|
/* Fennel: `require` in two-file fixture → IMPORTS edge.
|
|
* RED: grammar-only Fennel has no require-resolver in the pipeline. */
|
|
TEST(probe_fennel_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"util.fnl",
|
|
"(fn double [x] (* 2 x))\n"
|
|
"{:double double}\n"},
|
|
{"main.fnl",
|
|
"(local util (require :util))\n"
|
|
"\n"
|
|
"(fn run [n]\n"
|
|
" (util.double n))\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: Fennel `require` not resolved into IMPORTS edges. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 10 — Fish (.fish)
|
|
*
|
|
* Fish label histogram: Function:1, Module:1
|
|
* Fish shell scripting: `function` keyword; no imports, no OOP.
|
|
* The histogram shows only 1 function extracted (CALL_CASES fixture has 2 fns
|
|
* but only 1 reaches the graph — possible extraction gap for the second fn).
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Fish: single function definition → at least 1 Function node. */
|
|
TEST(probe_fish_function_node) {
|
|
GpaMetrics m = gpa_metrics("greet.fish",
|
|
"function greet\n"
|
|
" set name $argv[1]\n"
|
|
" echo \"Hello, $name!\"\n"
|
|
"end\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: the function must appear as a node. */
|
|
ASSERT_TRUE(m.functions >= 1);
|
|
PASS();
|
|
}
|
|
|
|
/* Fish: two function definitions — exercises the known histogram gap.
|
|
* The P5 CALL_CASES fixture has 2 fns but histogram shows Function:1.
|
|
* RED: second fish function not extracted (possible grammar gap in extractor). */
|
|
TEST(probe_fish_two_functions) {
|
|
GpaMetrics m = gpa_metrics("funcs.fish",
|
|
"function say_hello\n"
|
|
" echo \"Hello!\"\n"
|
|
"end\n"
|
|
"\n"
|
|
"function say_bye\n"
|
|
" echo \"Bye!\"\n"
|
|
"end\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: only 1 of the 2 fish functions appears to be extracted (histogram gap). */
|
|
ASSERT_TRUE(m.functions >= 2); /* expected RED — second fn not extracted */
|
|
PASS();
|
|
}
|
|
|
|
/* Fish has no OOP and no import mechanism; verify no spurious type nodes. */
|
|
TEST(probe_fish_no_type_nodes) {
|
|
GpaMetrics m = gpa_metrics("noop.fish",
|
|
"function noop\n"
|
|
" true\n"
|
|
"end\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: fish produces 0 type-like nodes. */
|
|
ASSERT_TRUE(m.types == 0);
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 11 — F# (.fs)
|
|
*
|
|
* F# label histogram: Function:2, Module:1
|
|
* F# is a functional-first .NET language: `let` functions, `type` records/unions,
|
|
* `open` for imports, `type Child() = inherit Parent()` for OOP.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* F#: two `let` function bindings → 2 Function nodes. */
|
|
TEST(probe_fsharp_functions) {
|
|
GpaMetrics m = gpa_metrics("calc.fs",
|
|
"let square x = x * x\n"
|
|
"\n"
|
|
"let cube x = x * square x\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both let-bindings must produce Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* F#: record type definition → type-like node.
|
|
* RED: the grammar histogram shows only Function/Module; record types not yet
|
|
* extracted as Type/Struct nodes. */
|
|
TEST(probe_fsharp_record_type) {
|
|
GpaMetrics m = gpa_metrics("model.fs",
|
|
"type Point = { X: float; Y: float }\n"
|
|
"\n"
|
|
"let origin = { X = 0.0; Y = 0.0 }\n"
|
|
"\n"
|
|
"let dist (a: Point) (b: Point) =\n"
|
|
" let dx = a.X - b.X\n"
|
|
" let dy = a.Y - b.Y\n"
|
|
" sqrt (dx*dx + dy*dy)\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: F# record type not yet extracted as a Type/Struct node. */
|
|
ASSERT_TRUE(m.types >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* F#: discriminated union → type-like node.
|
|
* RED: same root cause as record types. */
|
|
TEST(probe_fsharp_discriminated_union) {
|
|
GpaMetrics m = gpa_metrics("shape.fs",
|
|
"type Shape =\n"
|
|
" | Circle of float\n"
|
|
" | Rectangle of float * float\n"
|
|
"\n"
|
|
"let area shape =\n"
|
|
" match shape with\n"
|
|
" | Circle r -> System.Math.PI * r * r\n"
|
|
" | Rectangle (w, h) -> w * h\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: F# discriminated union not yet extracted as a Type node. */
|
|
ASSERT_TRUE(m.types >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* F#: class with `inherit` → Class node + INHERITS edge.
|
|
* RED: class nodes not in histogram; no F# INHERITS resolver. */
|
|
TEST(probe_fsharp_class_inherits) {
|
|
GpaMetrics m = gpa_metrics("oop.fs",
|
|
"type Animal(name: string) =\n"
|
|
" member _.Name = name\n"
|
|
" abstract member Speak: unit -> string\n"
|
|
" default _.Speak() = \"...\"\n"
|
|
"\n"
|
|
"type Dog(name: string) =\n"
|
|
" inherit Animal(name)\n"
|
|
" override _.Speak() = \"Woof!\"\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: F# OOP class/inherit not yet modeled by the extractor. */
|
|
ASSERT_TRUE(m.types >= 1); /* expected RED — no F# class extraction */
|
|
ASSERT_TRUE(m.inherits >= 1); /* expected RED — no F# INHERITS resolver */
|
|
PASS();
|
|
}
|
|
|
|
/* F#: `open` in two-file fixture → IMPORTS edge.
|
|
* RED: grammar-only F# has no `open`-resolver in the pipeline. */
|
|
TEST(probe_fsharp_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"MathUtils.fs",
|
|
"module MathUtils\n"
|
|
"\n"
|
|
"let double x = x * 2\n"
|
|
"let triple x = x * 3\n"},
|
|
{"Main.fs",
|
|
"module Main\n"
|
|
"\n"
|
|
"open MathUtils\n"
|
|
"\n"
|
|
"let run n = double n + triple n\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: F# `open` not resolved into IMPORTS edges. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* GROUP 12 — Gleam (.gleam)
|
|
*
|
|
* Gleam label histogram: Function:2, Module:1
|
|
* Gleam is a statically-typed functional language on the BEAM (Erlang VM).
|
|
* `pub fn` for public functions, `import` for modules; no OOP classes.
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
/* Gleam: two `pub fn` definitions → 2 Function nodes. */
|
|
TEST(probe_gleam_functions) {
|
|
GpaMetrics m = gpa_metrics("math.gleam",
|
|
"pub fn square(x: Int) -> Int {\n"
|
|
" x * x\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub fn cube(x: Int) -> Int {\n"
|
|
" x * square(x)\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both pub fn definitions must produce Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Gleam: private (non-pub) function — must also produce a Function node. */
|
|
TEST(probe_gleam_private_function) {
|
|
GpaMetrics m = gpa_metrics("util.gleam",
|
|
"fn internal_double(x: Int) -> Int {\n"
|
|
" x * 2\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub fn double(x: Int) -> Int {\n"
|
|
" internal_double(x)\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* GREEN: both the private and public fn must become Function nodes. */
|
|
ASSERT_TRUE(m.functions >= 2);
|
|
PASS();
|
|
}
|
|
|
|
/* Gleam: custom type definition → type-like node.
|
|
* RED: histogram shows Function/Module only; custom types not yet extracted. */
|
|
TEST(probe_gleam_custom_type) {
|
|
GpaMetrics m = gpa_metrics("types.gleam",
|
|
"pub type Shape {\n"
|
|
" Circle(Float)\n"
|
|
" Rectangle(Float, Float)\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub fn area(s: Shape) -> Float {\n"
|
|
" case s {\n"
|
|
" Circle(r) -> 3.14159 *. r *. r\n"
|
|
" Rectangle(w, h) -> w *. h\n"
|
|
" }\n"
|
|
"}\n");
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: Gleam custom type not yet extracted as a Type node. */
|
|
ASSERT_TRUE(m.types >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* Gleam: `import` in two-file fixture → IMPORTS edge.
|
|
* RED: grammar-only Gleam has no import-resolver in the pipeline. */
|
|
TEST(probe_gleam_imports_edge) {
|
|
static const GpaFile files[] = {
|
|
{"math_utils.gleam",
|
|
"pub fn double(x: Int) -> Int {\n"
|
|
" x * 2\n"
|
|
"}\n"},
|
|
{"main.gleam",
|
|
"import math_utils\n"
|
|
"\n"
|
|
"pub fn run(n: Int) -> Int {\n"
|
|
" math_utils.double(n)\n"
|
|
"}\n"}
|
|
};
|
|
GpaMetrics m = gpa_metrics_files(files, 2);
|
|
ASSERT_TRUE(m.ok);
|
|
/* RED: Gleam `import` not resolved into IMPORTS edges. */
|
|
ASSERT_TRUE(m.imports >= 1); /* expected RED */
|
|
PASS();
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════════════
|
|
* SUITE wiring
|
|
* ══════════════════════════════════════════════════════════════════ */
|
|
|
|
SUITE(grammar_probe_a) {
|
|
/* Ada */
|
|
RUN_TEST(probe_ada_functions);
|
|
RUN_TEST(probe_ada_package_spec_nodes);
|
|
RUN_TEST(probe_ada_no_crash);
|
|
RUN_TEST(probe_ada_imports_edge);
|
|
|
|
/* Awk */
|
|
RUN_TEST(probe_awk_functions);
|
|
RUN_TEST(probe_awk_three_functions);
|
|
RUN_TEST(probe_awk_no_type_nodes);
|
|
|
|
/* Cairo */
|
|
RUN_TEST(probe_cairo_functions);
|
|
RUN_TEST(probe_cairo_trait_or_type);
|
|
RUN_TEST(probe_cairo_three_functions);
|
|
|
|
/* Clojure */
|
|
RUN_TEST(probe_clojure_functions);
|
|
RUN_TEST(probe_clojure_defmulti);
|
|
RUN_TEST(probe_clojure_defrecord);
|
|
RUN_TEST(probe_clojure_imports_edge);
|
|
|
|
/* Common Lisp */
|
|
RUN_TEST(probe_commonlisp_functions);
|
|
RUN_TEST(probe_commonlisp_defmacro);
|
|
RUN_TEST(probe_commonlisp_defstruct);
|
|
RUN_TEST(probe_commonlisp_imports_edge);
|
|
|
|
/* Crystal */
|
|
RUN_TEST(probe_crystal_class_node);
|
|
RUN_TEST(probe_crystal_subclass_node);
|
|
RUN_TEST(probe_crystal_inherits_edge);
|
|
RUN_TEST(probe_crystal_module_node);
|
|
RUN_TEST(probe_crystal_imports_edge);
|
|
|
|
/* D */
|
|
RUN_TEST(probe_d_functions);
|
|
RUN_TEST(probe_d_struct_node);
|
|
RUN_TEST(probe_d_class_inherits);
|
|
RUN_TEST(probe_d_interface_node);
|
|
RUN_TEST(probe_d_imports_edge);
|
|
|
|
/* Emacs Lisp */
|
|
RUN_TEST(probe_emacslisp_functions);
|
|
RUN_TEST(probe_emacslisp_defvar);
|
|
RUN_TEST(probe_emacslisp_imports_edge);
|
|
|
|
/* Fennel */
|
|
RUN_TEST(probe_fennel_functions);
|
|
RUN_TEST(probe_fennel_lambda);
|
|
RUN_TEST(probe_fennel_imports_edge);
|
|
|
|
/* Fish */
|
|
RUN_TEST(probe_fish_function_node);
|
|
RUN_TEST(probe_fish_two_functions);
|
|
RUN_TEST(probe_fish_no_type_nodes);
|
|
|
|
/* F# */
|
|
RUN_TEST(probe_fsharp_functions);
|
|
RUN_TEST(probe_fsharp_record_type);
|
|
RUN_TEST(probe_fsharp_discriminated_union);
|
|
RUN_TEST(probe_fsharp_class_inherits);
|
|
RUN_TEST(probe_fsharp_imports_edge);
|
|
|
|
/* Gleam */
|
|
RUN_TEST(probe_gleam_functions);
|
|
RUN_TEST(probe_gleam_private_function);
|
|
RUN_TEST(probe_gleam_custom_type);
|
|
RUN_TEST(probe_gleam_imports_edge);
|
|
}
|