Files
whetstone_DSL/editor/tests/step316_test.cpp
Bill 0d51a6fe4c Steps 309-319: Sprint 11 Phases 11d-e — Kotlin/C# languages + workflow annotation foundation
Phase 11d (Steps 309-313): Kotlin + C# parsers and generators
- KotlinParser (regex-based): fun, suspend fun, class, data class, val/var
- KotlinGenerator: idiomatic Kotlin output with type mappings
- CSharpParser (regex-based): methods, async, class, interface
- CSharpGenerator: Allman braces, foreach, Task async, LINQ types
- Pipeline integration for both languages, 10 parsers + 10 generators

Phase 11e (Steps 314-319): Workflow annotation foundation
- AnnotationInference: generalized multi-subject inference engine
- Subject 9 routing annotations: ContextWidth, Review, Ambiguity,
  Automatability, Priority, ImplementationStatus
- SkeletonAST: project specification before code exists
- Architect tooling: createSkeleton, addSkeletonNode, getProjectModel,
  inferAnnotations RPCs + 4 MCP tools (42+ total)
- Inference-to-routing bridge: complexity→ambiguity, getter→deterministic
- TrainingDataExporter + TrainingDataGenerator scaffolding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:50:06 -07:00

316 lines
12 KiB
C++

// Step 316: Skeleton AST Support (12 tests)
// createSkeletonModule, addSkeletonFunction, addSkeletonClass,
// isSkeletonNode, getSkeletonSummary, skeletonToTaskList.
#include "SkeletonAST.h"
#include "ast/Module.h"
#include "ast/Function.h"
#include "ast/Annotation.h"
#include "ast/ClassDeclaration.h"
#include "ast/Statement.h"
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <algorithm>
static int passed = 0, failed = 0;
#define TEST(name) { std::cout << " " << #name << "... "; }
#define PASS() { std::cout << "PASS\n"; ++passed; }
#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; }
#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {}
// 1. Create skeleton module
void test_create_skeleton_module() {
TEST(create_skeleton_module);
auto* mod = createSkeletonModule("myapp", "python");
CHECK(mod != nullptr, "Module created");
CHECK(mod->name == "myapp", "Module name");
CHECK(mod->targetLanguage == "python", "Module language");
CHECK(mod->conceptType == "Module", "Module conceptType");
delete mod;
PASS();
}
// 2. Add skeleton function with parameters
void test_add_skeleton_function() {
TEST(add_skeleton_function);
auto* mod = createSkeletonModule("myapp", "python");
auto* fn = addSkeletonFunction(mod, "process", {"data", "config"}, "Result");
CHECK(fn != nullptr, "Function created");
CHECK(fn->name == "process", "Function name");
CHECK(fn->name == "process", "Function name matches");
auto params = fn->getChildren("parameters");
CHECK(params.size() == 2, "2 parameters, got: " + std::to_string(params.size()));
// Should auto-add ImplementationStatus(skeleton)
auto annos = fn->getChildren("annotations");
bool hasImplStatus = false;
for (auto* a : annos) {
if (a->conceptType == "ImplementationStatusAnnotation") {
auto* is = static_cast<ImplementationStatusAnnotation*>(a);
CHECK(is->status == "skeleton", "Auto-added skeleton status");
hasImplStatus = true;
}
}
CHECK(hasImplStatus, "ImplementationStatus auto-added");
delete mod;
PASS();
}
// 3. Add skeleton function with explicit annotations
void test_skeleton_function_with_annotations() {
TEST(skeleton_function_with_annotations);
auto* mod = createSkeletonModule("myapp", "python");
auto* cw = new ContextWidthAnnotation();
cw->id = "cw1"; cw->width = "project";
auto* aa = new AutomatabilityAnnotation();
aa->id = "aa1"; aa->strategy = "llm"; aa->confidence = 0.9;
auto* fn = addSkeletonFunction(mod, "analyze", {"input"}, "Analysis",
{cw, aa});
auto annos = fn->getChildren("annotations");
// Should have: ContextWidth, Automatability, ImplementationStatus(skeleton)
CHECK(annos.size() == 3, "3 annotations, got: " + std::to_string(annos.size()));
delete mod;
PASS();
}
// 4. Add skeleton class
void test_add_skeleton_class() {
TEST(add_skeleton_class);
auto* mod = createSkeletonModule("myapp", "python");
auto* cls = addSkeletonClass(mod, "UserService",
{"create", "delete", "find"},
{"db", "cache"});
CHECK(cls != nullptr, "Class created");
CHECK(cls->name == "UserService", "Class name");
auto methods = cls->getChildren("methods");
CHECK(methods.size() == 3, "3 methods");
auto fields = cls->getChildren("fields");
CHECK(fields.size() == 2, "2 fields");
// Each method should have ImplementationStatus(skeleton)
for (auto* m : methods) {
CHECK(isSkeletonNode(m), "Method " + m->id + " is skeleton");
}
delete mod;
PASS();
}
// 5. isSkeletonNode detection
void test_is_skeleton_node() {
TEST(is_skeleton_node);
// Skeleton: has ImplementationStatus(skeleton)
auto fn1 = std::make_unique<Function>("fn1", "empty_func");
auto* is = new ImplementationStatusAnnotation();
is->id = "is1"; is->status = "skeleton";
fn1->addChild("annotations", is);
CHECK(isSkeletonNode(fn1.get()), "Explicit skeleton annotation");
// Skeleton: empty body
auto fn2 = std::make_unique<Function>("fn2", "no_body");
CHECK(isSkeletonNode(fn2.get()), "Empty body = skeleton");
// Not skeleton: has body
auto fn3 = std::make_unique<Function>("fn3", "has_body");
auto* ret = new Return();
ret->id = "r1";
fn3->addChild("body", ret);
CHECK(!isSkeletonNode(fn3.get()), "Has body = not skeleton");
// Not skeleton: status = "complete"
auto fn4 = std::make_unique<Function>("fn4", "complete_func");
auto* is2 = new ImplementationStatusAnnotation();
is2->id = "is2"; is2->status = "complete";
fn4->addChild("annotations", is2);
auto* ret2 = new Return();
ret2->id = "r2";
fn4->addChild("body", ret2);
CHECK(!isSkeletonNode(fn4.get()), "Complete status = not skeleton");
PASS();
}
// 6. Skeleton summary counts
void test_skeleton_summary() {
TEST(skeleton_summary);
auto* mod = createSkeletonModule("myapp", "python");
addSkeletonFunction(mod, "task_a", {}, "void");
addSkeletonFunction(mod, "task_b", {}, "void");
// Add an implemented function
auto* implFn = new Function("fn3", "implemented");
auto* ret = new Return();
ret->id = "r1";
implFn->addChild("body", ret);
auto* completeStatus = new ImplementationStatusAnnotation();
completeStatus->id = "cis1"; completeStatus->status = "complete";
implFn->addChild("annotations", completeStatus);
mod->addChild("functions", implFn);
auto summary = getSkeletonSummary(mod);
CHECK(summary.totalNodes == 3, "3 total, got: " + std::to_string(summary.totalNodes));
CHECK(summary.skeletonNodes == 2, "2 skeleton, got: " + std::to_string(summary.skeletonNodes));
CHECK(summary.implementedNodes == 1, "1 implemented, got: " + std::to_string(summary.implementedNodes));
CHECK(summary.byAnnotationType.count("ImplementationStatusAnnotation") > 0, "Has impl status count");
delete mod;
PASS();
}
// 7. Task list generation
void test_task_list_generation() {
TEST(task_list_generation);
auto* mod = createSkeletonModule("myapp", "python");
auto* cw = new ContextWidthAnnotation();
cw->id = "cw1"; cw->width = "file";
auto* aa = new AutomatabilityAnnotation();
aa->id = "aa1"; aa->strategy = "template";
addSkeletonFunction(mod, "simple_getter", {}, "int", {cw, aa});
auto* cw2 = new ContextWidthAnnotation();
cw2->id = "cw2"; cw2->width = "project";
auto* aa2 = new AutomatabilityAnnotation();
aa2->id = "aa2"; aa2->strategy = "llm";
auto* pa = new PriorityAnnotation();
pa->id = "pa1"; pa->level = "critical";
pa->blockedBy.push_back("simple_getter");
addSkeletonFunction(mod, "complex_analysis", {"data"}, "Report", {cw2, aa2, pa});
auto tasks = skeletonToTaskList(mod);
CHECK(tasks.size() == 2, "2 tasks, got: " + std::to_string(tasks.size()));
auto& t1 = tasks[0];
CHECK(t1.nodeName == "simple_getter", "Task 1 name");
CHECK(t1.contextWidth == "file", "Task 1 context width");
CHECK(t1.automatability == "template", "Task 1 automatability");
auto& t2 = tasks[1];
CHECK(t2.nodeName == "complex_analysis", "Task 2 name");
CHECK(t2.contextWidth == "project", "Task 2 context width");
CHECK(t2.automatability == "llm", "Task 2 automatability");
CHECK(t2.priority == "critical", "Task 2 priority");
CHECK(t2.dependencies.size() == 1, "Task 2 has 1 dependency");
CHECK(t2.dependencies[0] == "simple_getter", "Task 2 blocked by simple_getter");
delete mod;
PASS();
}
// 8. Annotation extraction — defaults
void test_annotation_defaults() {
TEST(annotation_defaults);
auto* mod = createSkeletonModule("myapp", "python");
addSkeletonFunction(mod, "bare_function", {}, "void");
auto tasks = skeletonToTaskList(mod);
CHECK(tasks.size() == 1, "1 task");
auto& t = tasks[0];
CHECK(t.contextWidth == "local", "Default contextWidth = local");
CHECK(t.automatability == "llm", "Default automatability = llm");
CHECK(t.priority == "medium", "Default priority = medium");
CHECK(t.reviewRequired == false, "Default reviewRequired = false");
CHECK(t.status == "skeleton", "Default status = skeleton");
delete mod;
PASS();
}
// 9. Empty module handling
void test_empty_module() {
TEST(empty_module);
auto* mod = createSkeletonModule("empty", "python");
auto summary = getSkeletonSummary(mod);
CHECK(summary.totalNodes == 0, "0 total nodes");
CHECK(summary.skeletonNodes == 0, "0 skeleton nodes");
auto tasks = skeletonToTaskList(mod);
CHECK(tasks.empty(), "0 tasks");
delete mod;
PASS();
}
// 10. Dependency ordering from @Priority.blockedBy
void test_dependency_ordering() {
TEST(dependency_ordering);
auto* mod = createSkeletonModule("myapp", "python");
// Task C depends on A and B
auto* paC = new PriorityAnnotation();
paC->id = "paC"; paC->level = "low";
paC->blockedBy.push_back("task_a");
paC->blockedBy.push_back("task_b");
addSkeletonFunction(mod, "task_c", {}, "void", {paC});
addSkeletonFunction(mod, "task_a", {}, "void");
addSkeletonFunction(mod, "task_b", {}, "void");
auto tasks = skeletonToTaskList(mod);
CHECK(tasks.size() == 3, "3 tasks");
// Find task_c and verify dependencies
for (const auto& t : tasks) {
if (t.nodeName == "task_c") {
CHECK(t.dependencies.size() == 2, "task_c has 2 deps");
CHECK(t.dependencies[0] == "task_a", "dep[0] = task_a");
CHECK(t.dependencies[1] == "task_b", "dep[1] = task_b");
}
}
delete mod;
PASS();
}
// 11. Review inferred from high ambiguity
void test_review_from_ambiguity() {
TEST(review_from_ambiguity);
auto* mod = createSkeletonModule("myapp", "python");
auto* amb = new AmbiguityAnnotation();
amb->id = "amb1"; amb->level = "high";
amb->description = "Requirements are unclear";
addSkeletonFunction(mod, "unclear_task", {}, "void", {amb});
auto tasks = skeletonToTaskList(mod);
CHECK(tasks.size() == 1, "1 task");
CHECK(tasks[0].reviewRequired == true, "High ambiguity → review required");
delete mod;
PASS();
}
// 12. Class methods appear in task list
void test_class_methods_in_tasks() {
TEST(class_methods_in_tasks);
auto* mod = createSkeletonModule("myapp", "python");
addSkeletonClass(mod, "Service", {"init", "start", "stop"}, {});
auto tasks = skeletonToTaskList(mod);
// Should have: ClassDeclaration + 3 MethodDeclarations = 4 tasks
CHECK(tasks.size() == 4, "4 tasks (1 class + 3 methods), got: " + std::to_string(tasks.size()));
bool hasClass = false;
int methodCount = 0;
for (const auto& t : tasks) {
if (t.nodeType == "ClassDeclaration") hasClass = true;
if (t.nodeType == "MethodDeclaration") methodCount++;
}
CHECK(hasClass, "Has class task");
CHECK(methodCount == 3, "3 method tasks");
delete mod;
PASS();
}
int main() {
std::cout << "Step 316: Skeleton AST Support\n";
test_create_skeleton_module();
test_add_skeleton_function();
test_skeleton_function_with_annotations();
test_add_skeleton_class();
test_is_skeleton_node();
test_skeleton_summary();
test_task_list_generation();
test_annotation_defaults();
test_empty_module();
test_dependency_ordering();
test_review_from_ambiguity();
test_class_methods_in_tasks();
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
return failed > 0 ? 1 : 0;
}