Step 404: self-hosting phase integration and progress report
This commit is contained in:
@@ -2515,4 +2515,13 @@ target_link_libraries(step403_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step404_test tests/step404_test.cpp)
|
||||
target_include_directories(step404_test PRIVATE src)
|
||||
target_link_libraries(step404_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)
|
||||
|
||||
219
editor/tests/step404_test.cpp
Normal file
219
editor/tests/step404_test.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
// Step 404: Self-Hosting Progress Report + Phase Integration (8 tests)
|
||||
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "AnnotationInference.h"
|
||||
#include "Pipeline.h"
|
||||
#include "SelfHostHarness.h"
|
||||
#include "SemannoFormat.h"
|
||||
#include "ast/Annotation.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct AggregatedCoverage {
|
||||
int expected = 0;
|
||||
int parsed = 0;
|
||||
int opaque = 0;
|
||||
|
||||
double ratio() const {
|
||||
return expected > 0 ? static_cast<double>(parsed) / expected : 1.0;
|
||||
}
|
||||
};
|
||||
|
||||
std::string resolvePath(const std::string& name) {
|
||||
const std::vector<std::string> candidates = {
|
||||
"src/" + name,
|
||||
"editor/src/" + name,
|
||||
"../src/" + name,
|
||||
"../../editor/src/" + name
|
||||
};
|
||||
for (const auto& candidate : candidates) {
|
||||
if (std::filesystem::exists(candidate)) return candidate;
|
||||
}
|
||||
return "src/" + name;
|
||||
}
|
||||
|
||||
std::string conflictHeaderPath() {
|
||||
static const std::string path = resolvePath("AnnotationConflictExtended.h");
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string validatorHeaderPath() {
|
||||
static const std::string path = resolvePath("AnnotationValidatorExtended.h");
|
||||
return path;
|
||||
}
|
||||
|
||||
ExpectedStructure expectedConflict() {
|
||||
ExpectedStructure expected;
|
||||
expected.filePath = conflictHeaderPath();
|
||||
expected.constructs = {
|
||||
{"struct", "CrossTypeConflict"},
|
||||
{"function", "collectCrossTypeConflicts"}
|
||||
};
|
||||
return expected;
|
||||
}
|
||||
|
||||
ExpectedStructure expectedValidator() {
|
||||
ExpectedStructure expected;
|
||||
expected.filePath = validatorHeaderPath();
|
||||
expected.constructs = {
|
||||
{"class", "AnnotationValidatorExtended"}
|
||||
};
|
||||
return expected;
|
||||
}
|
||||
|
||||
AggregatedCoverage aggregate(const SelfHostResult& a, const SelfHostResult& b) {
|
||||
AggregatedCoverage out;
|
||||
out.expected = a.totalExpected + b.totalExpected;
|
||||
out.parsed = a.totalParsed + b.totalParsed;
|
||||
out.opaque = a.totalOpaque + b.totalOpaque;
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<std::string> collectGaps(const SelfHostResult& result) {
|
||||
std::vector<std::string> gaps;
|
||||
for (const auto& cov : result.coverage) {
|
||||
if (cov.opaque > 0) {
|
||||
gaps.push_back(cov.constructType + ":" + std::to_string(cov.opaque));
|
||||
}
|
||||
}
|
||||
return gaps;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
int passed = 0;
|
||||
|
||||
// Test 1: Self-host coverage for both target headers.
|
||||
{
|
||||
auto conflict = SelfHostHarness::parseFile(conflictHeaderPath());
|
||||
auto validator = SelfHostHarness::parseFile(validatorHeaderPath());
|
||||
auto expectConflict = expectedConflict();
|
||||
auto expectValidator = expectedValidator();
|
||||
SelfHostHarness::checkExpected(conflict, expectConflict);
|
||||
SelfHostHarness::checkExpected(validator, expectValidator);
|
||||
assert(conflict.totalParsed == 2);
|
||||
assert(validator.totalParsed == 1);
|
||||
std::cout << "PASS: test 1 — per-file self-host coverage metrics\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 2: Per-file coverage ratios are at full baseline.
|
||||
{
|
||||
auto conflict = SelfHostHarness::parseFile(conflictHeaderPath());
|
||||
auto validator = SelfHostHarness::parseFile(validatorHeaderPath());
|
||||
SelfHostHarness::checkExpected(conflict, expectedConflict());
|
||||
SelfHostHarness::checkExpected(validator, expectedValidator());
|
||||
assert(conflict.overallCoverage() == 1.0);
|
||||
assert(validator.overallCoverage() == 1.0);
|
||||
std::cout << "PASS: test 2 — baseline coverage ratios at 100%\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 3: Combined phase coverage aggregates correctly.
|
||||
{
|
||||
auto conflict = SelfHostHarness::parseFile(conflictHeaderPath());
|
||||
auto validator = SelfHostHarness::parseFile(validatorHeaderPath());
|
||||
SelfHostHarness::checkExpected(conflict, expectedConflict());
|
||||
SelfHostHarness::checkExpected(validator, expectedValidator());
|
||||
auto totals = aggregate(conflict, validator);
|
||||
assert(totals.expected == 3);
|
||||
assert(totals.parsed == 3);
|
||||
assert(totals.opaque == 0);
|
||||
assert(totals.ratio() == 1.0);
|
||||
std::cout << "PASS: test 3 — aggregate coverage accounting\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 4: Pipeline end-to-end succeeds on both self-host headers.
|
||||
{
|
||||
Pipeline pipeline;
|
||||
auto conflictSource = SelfHostHarness::readFile(conflictHeaderPath());
|
||||
auto validatorSource = SelfHostHarness::readFile(validatorHeaderPath());
|
||||
auto conflictRun = pipeline.run(conflictSource, "cpp", "cpp");
|
||||
auto validatorRun = pipeline.run(validatorSource, "cpp", "cpp");
|
||||
assert(conflictRun.success && conflictRun.ast != nullptr && !conflictRun.generatedCode.empty());
|
||||
assert(validatorRun.success && validatorRun.ast != nullptr && !validatorRun.generatedCode.empty());
|
||||
std::cout << "PASS: test 4 — pipeline runs for both self-host files\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 5: Gap-analysis reporting captures opaque construct deficits.
|
||||
{
|
||||
auto conflict = SelfHostHarness::parseFile(conflictHeaderPath());
|
||||
ExpectedStructure strictExpected;
|
||||
strictExpected.filePath = conflictHeaderPath();
|
||||
strictExpected.constructs = {
|
||||
{"struct", "CrossTypeConflict"},
|
||||
{"function", "collectCrossTypeConflicts"},
|
||||
{"class", "MissingClassForGapReport"},
|
||||
{"function", "missingHelperForGapReport"}
|
||||
};
|
||||
SelfHostHarness::checkExpected(conflict, strictExpected);
|
||||
auto gaps = collectGaps(conflict);
|
||||
assert(conflict.totalOpaque == 2);
|
||||
assert(gaps.size() >= 1);
|
||||
std::cout << "PASS: test 5 — gap-analysis report path\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 6: Inferred complexity annotation emits valid Semanno.
|
||||
{
|
||||
auto validator = SelfHostHarness::parseFile(validatorHeaderPath());
|
||||
AnnotationInference inf;
|
||||
auto inferred = inf.inferAll(validator.ast.get());
|
||||
std::string complexity = "O(1)";
|
||||
for (const auto& item : inferred) {
|
||||
if (item.annotationType == "ComplexityAnnotation") {
|
||||
complexity = item.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ComplexityAnnotation anno;
|
||||
anno.timeComplexity = complexity;
|
||||
const std::string semanno = SemannoEmitter::emit(&anno);
|
||||
assert(SemannoParser::isSemannoComment(semanno));
|
||||
auto parsed = SemannoParser::parse(semanno);
|
||||
assert(parsed.type == "complexity");
|
||||
std::cout << "PASS: test 6 — semanno output for inferred complexity\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 7: Coverage reports include file identity and parse state.
|
||||
{
|
||||
auto conflict = SelfHostHarness::parseFile(conflictHeaderPath());
|
||||
SelfHostHarness::checkExpected(conflict, expectedConflict());
|
||||
auto report = SelfHostHarness::coverageReport(conflict);
|
||||
assert(report.find(conflictHeaderPath()) != std::string::npos);
|
||||
assert(report.find("Parse success: yes") != std::string::npos);
|
||||
std::cout << "PASS: test 7 — report formatting includes file + status\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 8: Phase artifact sanity (steps 400-404 test files exist).
|
||||
{
|
||||
const std::vector<std::string> required = {
|
||||
"tests/step400_test.cpp",
|
||||
"tests/step401_test.cpp",
|
||||
"tests/step402_test.cpp",
|
||||
"tests/step403_test.cpp",
|
||||
"tests/step404_test.cpp"
|
||||
};
|
||||
int found = 0;
|
||||
for (const auto& rel : required) {
|
||||
if (std::filesystem::exists("editor/" + rel)) found++;
|
||||
}
|
||||
assert(found == 5);
|
||||
std::cout << "PASS: test 8 — phase 16b artifact set present\n";
|
||||
passed++;
|
||||
}
|
||||
|
||||
std::cout << "\nStep 404 result: " << passed << "/8 tests passed\n";
|
||||
return (passed == 8) ? 0 : 1;
|
||||
}
|
||||
48
progress.md
48
progress.md
@@ -3572,6 +3572,54 @@ complexity metadata.
|
||||
- `editor/src/MCPServer.h` (`1679` > `600`)
|
||||
- `editor/src/HeadlessAgentRPCHandler.h` (`2623` > `600`)
|
||||
|
||||
### Step 404: Self-Hosting Progress Report + Phase Integration
|
||||
**Status:** PASS (8/8 tests)
|
||||
|
||||
Completed Phase 16b integration reporting with aggregate self-host coverage,
|
||||
pipeline execution checks over both target headers, Semanno roundtrip checks
|
||||
from inferred complexity metadata, and explicit gap-reporting behavior.
|
||||
|
||||
**Files created:**
|
||||
- `editor/tests/step404_test.cpp` — 8 integration tests covering:
|
||||
1. per-file self-host coverage metrics
|
||||
2. baseline per-file coverage ratios
|
||||
3. aggregate phase coverage accounting
|
||||
4. `Pipeline.run()` success for both target headers
|
||||
5. gap-reporting path for opaque construct deficits
|
||||
6. Semanno emit/parse check for inferred complexity
|
||||
7. report formatting (file identity + parse status)
|
||||
8. phase artifact sanity (step400-404 test files present)
|
||||
|
||||
**Files modified:**
|
||||
- `editor/CMakeLists.txt` — `step404_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `step404_test` — PASS (8/8) new integration coverage
|
||||
- `step403_test` — PASS (12/12) regression coverage
|
||||
- `step402_test` — PASS (12/12) regression coverage
|
||||
|
||||
**Phase 16b completion snapshot (Steps 400-404):**
|
||||
- Step 400: Self-hosting harness + coverage tracking (12 tests)
|
||||
- Step 401: Parse `AnnotationConflictExtended.h` (12 tests)
|
||||
- Step 402: Parse `AnnotationValidatorExtended.h` (12 tests)
|
||||
- Step 403: Inference + generation on self-hosted ASTs (12 tests)
|
||||
- Step 404: Coverage report + phase integration (8 tests)
|
||||
|
||||
**Sprint 16 completion snapshot (Steps 394-404):**
|
||||
- Phase 16a complete (Steps 394-399, 68 tests)
|
||||
- Phase 16b complete (Steps 400-404, 56 tests)
|
||||
- Sprint 16 total: 11 steps, 124 tests
|
||||
|
||||
**Architecture gate check (end of Sprint 16):**
|
||||
- `editor/tests/step404_test.cpp` within test-file size guidance (`219` lines)
|
||||
- `editor/tests/step403_test.cpp` within test-file size guidance (`236` lines)
|
||||
- `editor/tests/step402_test.cpp` within test-file size guidance (`239` lines)
|
||||
- `editor/src/SelfHostHarness.h` within header-size limit (`308` <= `600`)
|
||||
- `editor/src/AnnotationValidatorExtended.h` within header-size limit (`349` <= `600`)
|
||||
- Legacy oversized headers persist and remain queued for split work:
|
||||
- `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)
|
||||
|
||||
Reference in New Issue
Block a user