diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index f0bf4b4..b63d87d 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2497,4 +2497,13 @@ target_link_libraries(step401_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step402_test tests/step402_test.cpp) +target_include_directories(step402_test PRIVATE src) +target_link_libraries(step402_test PRIVATE + nlohmann_json::nlohmann_json + unofficial::tree-sitter::tree-sitter + tree_sitter_python tree_sitter_cpp tree_sitter_elisp + tree_sitter_javascript tree_sitter_typescript + tree_sitter_java tree_sitter_rust tree_sitter_go) + # Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies) diff --git a/editor/tests/step402_test.cpp b/editor/tests/step402_test.cpp new file mode 100644 index 0000000..16b9e87 --- /dev/null +++ b/editor/tests/step402_test.cpp @@ -0,0 +1,239 @@ +// Step 402: Parse AnnotationValidatorExtended.h (12 tests) +// Self-hosting: parse class-heavy validator header with static helpers and casts. + +#include +#include +#include +#include +#include + +#include "AnnotationInference.h" +#include "SelfHostHarness.h" +#include "ast/ClassDeclaration.h" +#include "ast/CppAdvancedNodes.h" +#include "ast/Function.h" + +namespace { + +std::string resolveHeaderPath() { + const std::vector candidates = { + "src/AnnotationValidatorExtended.h", + "editor/src/AnnotationValidatorExtended.h", + "../src/AnnotationValidatorExtended.h", + "../../editor/src/AnnotationValidatorExtended.h" + }; + for (const auto& candidate : candidates) { + if (std::filesystem::exists(candidate)) return candidate; + } + return "src/AnnotationValidatorExtended.h"; +} + +std::string headerPath() { + static const std::string path = resolveHeaderPath(); + return path; +} + +const ClassDeclaration* findClass(const Module* module, const std::string& name) { + if (!module) return nullptr; + for (auto* clsNode : module->getChildren("classes")) { + auto* cls = static_cast(clsNode); + if (cls->name == name) return cls; + } + return nullptr; +} + +const Function* findMethod(const ClassDeclaration* cls, const std::string& name) { + if (!cls) return nullptr; + for (auto* methodNode : cls->getChildren("methods")) { + if (methodNode->conceptType == "MethodDeclaration" || methodNode->conceptType == "Function") { + auto* method = static_cast(methodNode); + if (method->name == name) return method; + } + } + return nullptr; +} + +int countConceptRecursive(const ASTNode* node, const std::string& conceptType) { + if (!node) return 0; + int total = (node->conceptType == conceptType) ? 1 : 0; + for (auto* child : node->allChildren()) { + total += countConceptRecursive(child, conceptType); + } + return total; +} + +int countStaticMethods(const ClassDeclaration* cls) { + if (!cls) return 0; + int count = 0; + for (auto* methodNode : cls->getChildren("methods")) { + if (methodNode->conceptType == "MethodDeclaration") { + auto* method = static_cast(methodNode); + if (method->isStatic) count++; + } + } + return count; +} + +} // namespace + +int main() { + int passed = 0; + + // Test 1: Parse real validator header file from disk. + { + assert(std::filesystem::exists(headerPath())); + auto result = SelfHostHarness::parseFile(headerPath()); + assert(result.parseSuccess); + assert(result.ast != nullptr); + std::cout << "PASS: test 1 — parse real AnnotationValidatorExtended.h file\n"; + passed++; + } + + // Test 2: AnnotationValidatorExtended class is captured. + { + auto result = SelfHostHarness::parseFile(headerPath()); + assert(SelfHostHarness::hasParsedConstruct( + result.ast.get(), "class", "AnnotationValidatorExtended")); + std::cout << "PASS: test 2 — class AnnotationValidatorExtended captured\n"; + passed++; + } + + // Test 3: Class has parsed method entries. + { + auto result = SelfHostHarness::parseFile(headerPath()); + const auto* cls = findClass(result.ast.get(), "AnnotationValidatorExtended"); + assert(cls != nullptr); + assert(cls->getChildren("methods").size() >= 3); + std::cout << "PASS: test 3 — class has method entries\n"; + passed++; + } + + // Test 4: Public validate method signature is captured. + { + auto result = SelfHostHarness::parseFile(headerPath()); + const auto* cls = findClass(result.ast.get(), "AnnotationValidatorExtended"); + const auto* method = findMethod(cls, "validate"); + assert(method != nullptr); + assert(method->getChildren("parameters").size() >= 1); + std::cout << "PASS: test 4 — validate method signature captured\n"; + passed++; + } + + // Test 5: Private helper methods are captured. + { + auto result = SelfHostHarness::parseFile(headerPath()); + const auto* cls = findClass(result.ast.get(), "AnnotationValidatorExtended"); + assert(findMethod(cls, "validateNode") != nullptr); + assert(findMethod(cls, "validateAnnotation") != nullptr); + std::cout << "PASS: test 5 — private helper methods captured\n"; + passed++; + } + + // Test 6: Static helper methods are captured as static methods. + { + auto result = SelfHostHarness::parseFile(headerPath()); + const auto* cls = findClass(result.ast.get(), "AnnotationValidatorExtended"); + assert(findMethod(cls, "isPowerOf2") != nullptr); + assert(findMethod(cls, "isOneOf") != nullptr); + assert(countStaticMethods(cls) >= 2); + std::cout << "PASS: test 6 — static helper methods parsed\n"; + passed++; + } + + // Test 7: Self-host expected structure has full coverage. + { + auto result = SelfHostHarness::parseFile(headerPath()); + ExpectedStructure expected; + expected.filePath = headerPath(); + expected.constructs = {{"class", "AnnotationValidatorExtended"}}; + SelfHostHarness::checkExpected(result, expected); + assert(result.totalExpected == 1); + assert(result.totalParsed == 1); + assert(result.totalOpaque == 0); + assert(result.overallCoverage() == 1.0); + std::cout << "PASS: test 7 — expected structure coverage 100%\n"; + passed++; + } + + // Test 8: static_cast patterns from validator logic are recognized by cast helpers. + { + const std::string expr1 = "static_cast(anno)"; + const std::string expr2 = "static_cast(anno)"; + assert(detectCastKind(expr1) == "static_cast"); + assert(detectCastKind(expr2) == "static_cast"); + assert(castRiskLevel(detectCastKind(expr1)) == "low"); + std::cout << "PASS: test 8 — static_cast detection helpers match validator patterns\n"; + passed++; + } + + // Test 9: Inference produces complexity signals on parsed methods. + { + auto result = SelfHostHarness::parseFile(headerPath()); + AnnotationInference inf; + auto inferred = inf.inferAll(result.ast.get()); + int complexityCount = 0; + for (const auto& item : inferred) { + if (item.annotationType == "ComplexityAnnotation") complexityCount++; + } + assert(complexityCount >= 1); + std::cout << "PASS: test 9 — complexity inference runs on self-host AST\n"; + passed++; + } + + // Test 10: Targeted class snippet with static helpers parses cleanly. + { + const std::string source = R"( +class ValidatorLite { +public: + static bool ok(int x) { return x > 0; } + static bool positive(int x) { return x >= 0; } + int run(int value) { return value + 1; } +}; +)"; + auto result = SelfHostHarness::parseSource(source, "validator_lite"); + assert(result.parseSuccess); + assert(SelfHostHarness::hasParsedConstruct(result.ast.get(), "class", "ValidatorLite")); + const auto* cls = findClass(result.ast.get(), "ValidatorLite"); + assert(cls != nullptr); + assert(countStaticMethods(cls) >= 2); + std::cout << "PASS: test 10 — static helper snippet parse stable\n"; + passed++; + } + + // Test 11: Coverage report includes real file path and parse status. + { + auto result = SelfHostHarness::parseFile(headerPath()); + ExpectedStructure expected; + expected.filePath = headerPath(); + expected.constructs = {{"class", "AnnotationValidatorExtended"}}; + SelfHostHarness::checkExpected(result, expected); + const std::string report = SelfHostHarness::coverageReport(result); + assert(report.find(headerPath()) != std::string::npos); + assert(report.find("Parse success: yes") != std::string::npos); + assert(report.find("100%") != std::string::npos); + std::cout << "PASS: test 11 — coverage report content checks\n"; + passed++; + } + + // Test 12: Missing expected constructs are counted as opaque, not fatal. + { + auto result = SelfHostHarness::parseFile(headerPath()); + ExpectedStructure expected; + expected.filePath = headerPath(); + expected.constructs = { + {"class", "AnnotationValidatorExtended"}, + {"function", "definitelyMissingFunction"}, + {"class", "DefinitelyMissingClass"} + }; + SelfHostHarness::checkExpected(result, expected); + assert(result.totalExpected == 3); + assert(result.totalParsed == 1); + assert(result.totalOpaque == 2); + assert(result.overallCoverage() == (1.0 / 3.0)); + std::cout << "PASS: test 12 — graceful degradation via opaque tracking\n"; + passed++; + } + + std::cout << "\nStep 402 result: " << passed << "/12 tests passed\n"; + return (passed == 12) ? 0 : 1; +} diff --git a/progress.md b/progress.md index 6a9c387..04a2280 100644 --- a/progress.md +++ b/progress.md @@ -3496,6 +3496,45 @@ coverage reporting, and graceful degradation on missing expected constructs. - `editor/src/MCPServer.h` (`1679` > `600`) - `editor/src/HeadlessAgentRPCHandler.h` (`2623` > `600`) +### Step 402: Parse AnnotationValidatorExtended.h +**Status:** PASS (12/12 tests) + +Validated self-hosting parse coverage for a class-heavy production header +(`AnnotationValidatorExtended.h`): class/method extraction, static helper +method recognition, validator-style cast-pattern helper checks, complexity +inference on parsed AST, and graceful degradation accounting. + +**Files created:** +- `editor/tests/step402_test.cpp` — 12 tests covering: + 1. parse real header file from disk + 2. `AnnotationValidatorExtended` class capture + 3. class method extraction + 4. public `validate` signature capture + 5. private helper method capture (`validateNode`, `validateAnnotation`) + 6. static helper method parsing (`isPowerOf2`, `isOneOf`) + 7. expected-structure 100% coverage + 8. validator-style `static_cast` detection helper checks + 9. complexity inference execution on parsed AST + 10. static-helper class snippet parse stability + 11. coverage report content checks + 12. graceful degradation via opaque coverage accounting + +**Files modified:** +- `editor/CMakeLists.txt` — `step402_test` target + +**Verification run:** +- `step402_test` — PASS (12/12) new step coverage +- `step401_test` — PASS (12/12) regression coverage +- `step400_test` — PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/tests/step402_test.cpp` within test-file size guidance (`239` lines) +- `editor/src/AnnotationValidatorExtended.h` within header-size limit (`349` <= `600`) +- `editor/src/SelfHostHarness.h` remains within header-size limit (`308` <= `600`) +- Legacy oversized headers persist: + - `editor/src/MCPServer.h` (`1679` > `600`) + - `editor/src/HeadlessAgentRPCHandler.h` (`2623` > `600`) + # Roadmap Planning — Sprints 12-25+ ## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)