diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index c760c84..4c4f2d4 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3901,4 +3901,13 @@ target_link_libraries(step557_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step558_test tests/step558_test.cpp) +target_include_directories(step558_test PRIVATE src) +target_link_libraries(step558_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/src/Phase30aIntegration.h b/editor/src/Phase30aIntegration.h new file mode 100644 index 0000000..1d39958 --- /dev/null +++ b/editor/src/Phase30aIntegration.h @@ -0,0 +1,80 @@ +#pragma once +// Step 558: Phase 30a Integration + +#include +#include + +#include "DebugSessionModel.h" +#include "BreakpointSystem.h" +#include "StepEngine.h" +#include "ExceptionStackTraceCapture.h" + +struct Phase30aRequest { + std::string targetId; + std::string bufferId; + std::string executablePath; + std::string breakpointId; + int breakpointLine = 0; + bool triggerCondition = true; + std::string stepCommand; + bool triggerException = false; +}; + +struct Phase30aResult { + bool pass = false; + bool breakpointHit = false; + StepResult stepResult; + ExceptionCaptureResult exceptionResult; + std::vector errors; +}; + +class Phase30aIntegration { +public: + static Phase30aResult run(const Phase30aRequest& request) { + Phase30aResult out; + + DebugSessionModel session; + if (!session.start(request.targetId, request.bufferId, request.executablePath)) { + out.errors.push_back("session_start_failed"); + return out; + } + + BreakpointSystem breakpoints; + if (!breakpoints.addConditionalBreakpoint(request.breakpointId, + request.bufferId, + request.breakpointLine, + "runtime_condition")) { + out.errors.push_back("breakpoint_add_failed"); + return out; + } + + out.breakpointHit = breakpoints.shouldBreak(request.breakpointId, request.triggerCondition); + if (!out.breakpointHit) { + out.errors.push_back("breakpoint_not_hit"); + return out; + } + + StepEngine engine; + out.stepResult = engine.execute(request.stepCommand); + if (!out.stepResult.ok) { + out.errors.push_back("step_failed"); + return out; + } + + if (request.triggerException) { + out.exceptionResult = ExceptionStackTraceCapture::capture( + "RuntimeError", + "simulated exception", + "thread-main", + {{"run", request.bufferId, request.breakpointLine, ""}, + {"dispatch", request.bufferId, request.breakpointLine + 1, ""}}); + if (!out.exceptionResult.valid) { + out.errors.push_back("exception_capture_failed"); + return out; + } + } + + out.pass = true; + return out; + } +}; diff --git a/editor/tests/step558_test.cpp b/editor/tests/step558_test.cpp new file mode 100644 index 0000000..a19083d --- /dev/null +++ b/editor/tests/step558_test.cpp @@ -0,0 +1,117 @@ +// Step 558: Phase 30a Integration (8 tests) + +#include "Phase30aIntegration.h" + +#include + +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 {} + +static bool hasError(const Phase30aResult& r, const std::string& e) { + for (const auto& x : r.errors) if (x == e) return true; + return false; +} + +static Phase30aRequest baseReq() { + return {"target-1", "main.cpp", "/bin/app", "bp-1", 20, true, "step_over", false}; +} + +void test_full_cycle_without_exception_passes() { + TEST(full_cycle_without_exception_passes); + auto r = Phase30aIntegration::run(baseReq()); + CHECK(r.pass, "baseline phase30a cycle should pass"); + CHECK(r.breakpointHit, "breakpoint should be hit"); + PASS(); +} + +void test_full_cycle_with_exception_passes() { + TEST(full_cycle_with_exception_passes); + auto req = baseReq(); + req.triggerException = true; + auto r = Phase30aIntegration::run(req); + CHECK(r.pass, "exception cycle should pass"); + CHECK(r.exceptionResult.valid, "exception capture should be valid"); + PASS(); +} + +void test_invalid_breakpoint_line_fails() { + TEST(invalid_breakpoint_line_fails); + auto req = baseReq(); + req.breakpointLine = 0; + auto r = Phase30aIntegration::run(req); + CHECK(!r.pass, "invalid breakpoint line should fail"); + CHECK(hasError(r, "breakpoint_add_failed"), "breakpoint add error expected"); + PASS(); +} + +void test_condition_false_prevents_breakpoint_hit() { + TEST(condition_false_prevents_breakpoint_hit); + auto req = baseReq(); + req.triggerCondition = false; + auto r = Phase30aIntegration::run(req); + CHECK(!r.pass, "condition false should fail cycle"); + CHECK(hasError(r, "breakpoint_not_hit"), "breakpoint not hit error expected"); + PASS(); +} + +void test_unknown_step_command_fails_cycle() { + TEST(unknown_step_command_fails_cycle); + auto req = baseReq(); + req.stepCommand = "warp"; + auto r = Phase30aIntegration::run(req); + CHECK(!r.pass, "unknown step command should fail"); + CHECK(hasError(r, "step_failed"), "step failure expected"); + PASS(); +} + +void test_session_start_failure_when_target_invalid() { + TEST(session_start_failure_when_target_invalid); + auto req = baseReq(); + req.targetId = ""; + auto r = Phase30aIntegration::run(req); + CHECK(!r.pass, "invalid session start should fail"); + CHECK(hasError(r, "session_start_failed"), "session start failure expected"); + PASS(); +} + +void test_step_result_exposed_for_ui_sync() { + TEST(step_result_exposed_for_ui_sync); + auto req = baseReq(); + req.stepCommand = "continue"; + auto r = Phase30aIntegration::run(req); + CHECK(r.pass, "continue flow should pass"); + CHECK(r.stepResult.ok, "step result should be ok"); + CHECK(r.stepResult.state.lastCommand == "continue", "last command should propagate"); + PASS(); +} + +void test_exception_frame_anchor_uses_buffer_path() { + TEST(exception_frame_anchor_uses_buffer_path); + auto req = baseReq(); + req.triggerException = true; + auto r = Phase30aIntegration::run(req); + CHECK(r.pass, "exception flow should pass"); + CHECK(!r.exceptionResult.event.frames.empty(), "frames expected"); + CHECK(r.exceptionResult.event.frames[0].navigationAnchor.find("main.cpp:") == 0, + "anchor should use buffer path"); + PASS(); +} + +int main() { + std::cout << "Step 558: Phase 30a Integration\n"; + + test_full_cycle_without_exception_passes(); // 1 + test_full_cycle_with_exception_passes(); // 2 + test_invalid_breakpoint_line_fails(); // 3 + test_condition_false_prevents_breakpoint_hit(); // 4 + test_unknown_step_command_fails_cycle(); // 5 + test_session_start_failure_when_target_invalid();// 6 + test_step_result_exposed_for_ui_sync(); // 7 + test_exception_frame_anchor_uses_buffer_path(); // 8 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 97d8857..d26058b 100644 --- a/progress.md +++ b/progress.md @@ -9905,3 +9905,41 @@ editor navigation anchors for each stack frame. - `editor/src/ExceptionStackTraceCapture.h` within header-size limit (`71` <= `600`) - `editor/tests/step557_test.cpp` within test-file size guidance (`149` lines) - Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +### Step 558: Phase 30a Integration +**Status:** PASS (8/8 tests) + +Integrates debug session lifecycle, breakpoint handling, stepping, and exception +capture into a complete Phase 30a debug control-plane cycle. + +**Files added:** +- `editor/src/Phase30aIntegration.h` - Phase 30a integration gate: + - composes Step 554 session lifecycle, Step 555 breakpoint system, + Step 556 step engine, and Step 557 exception capture + - executes breakpoint-hit to step-command to optional exception flow + - emits integrated pass/fail state with explicit error reasons +- `editor/tests/step558_test.cpp` - 8 tests covering: + - full cycle pass paths (with and without exception) + - breakpoint add/hit failure paths + - step command failure path + - session start failure path + - step-result propagation and exception anchor linkage + +**Files modified:** +- `editor/CMakeLists.txt` - `step558_test` target + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step558_test step557_test` - PASS +- `./editor/build-native/step558_test` - PASS (8/8) +- `./editor/build-native/step557_test` - PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/Phase30aIntegration.h` within header-size limit (`80` <= `600`) +- `editor/tests/step558_test.cpp` within test-file size guidance (`117` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +**Phase 30a totals (554-558):** +- **Steps completed:** 5 +- **New tests in phase plan:** 56/56 passing +- **Debug control-plane integration:** PASS