Step 402: self-host parse coverage for AnnotationValidatorExtended
This commit is contained in:
@@ -2497,4 +2497,13 @@ target_link_libraries(step401_test PRIVATE
|
|||||||
tree_sitter_javascript tree_sitter_typescript
|
tree_sitter_javascript tree_sitter_typescript
|
||||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
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)
|
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
||||||
|
|||||||
239
editor/tests/step402_test.cpp
Normal file
239
editor/tests/step402_test.cpp
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
// Step 402: Parse AnnotationValidatorExtended.h (12 tests)
|
||||||
|
// Self-hosting: parse class-heavy validator header with static helpers and casts.
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<std::string> 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<ClassDeclaration*>(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<Function*>(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<MethodDeclaration*>(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<const BitWidthAnnotation*>(anno)";
|
||||||
|
const std::string expr2 = "static_cast<const LayoutAnnotation*>(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;
|
||||||
|
}
|
||||||
39
progress.md
39
progress.md
@@ -3496,6 +3496,45 @@ coverage reporting, and graceful degradation on missing expected constructs.
|
|||||||
- `editor/src/MCPServer.h` (`1679` > `600`)
|
- `editor/src/MCPServer.h` (`1679` > `600`)
|
||||||
- `editor/src/HeadlessAgentRPCHandler.h` (`2623` > `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+
|
# Roadmap Planning — Sprints 12-25+
|
||||||
|
|
||||||
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)
|
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)
|
||||||
|
|||||||
Reference in New Issue
Block a user