Step 559: add call stack frame inspector model
This commit is contained in:
@@ -3910,4 +3910,13 @@ target_link_libraries(step558_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step559_test tests/step559_test.cpp)
|
||||
target_include_directories(step559_test PRIVATE src)
|
||||
target_link_libraries(step559_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)
|
||||
|
||||
58
editor/src/CallStackFrameInspector.h
Normal file
58
editor/src/CallStackFrameInspector.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
// Step 559: Call Stack and Frame Inspector
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct FrameScope {
|
||||
int frameIndex = 0;
|
||||
std::string functionName;
|
||||
std::string filePath;
|
||||
int line = 0;
|
||||
std::map<std::string, std::string> variables;
|
||||
};
|
||||
|
||||
struct FrameInspectorResult {
|
||||
bool ok = false;
|
||||
int selectedIndex = -1;
|
||||
FrameScope selected;
|
||||
std::vector<std::string> errors;
|
||||
};
|
||||
|
||||
class CallStackFrameInspector {
|
||||
public:
|
||||
static FrameInspectorResult inspect(const std::vector<FrameScope>& frames,
|
||||
int requestedIndex) {
|
||||
FrameInspectorResult out;
|
||||
if (frames.empty()) {
|
||||
out.errors.push_back("empty_stack");
|
||||
return out;
|
||||
}
|
||||
if (requestedIndex < 0 || requestedIndex >= (int)frames.size()) {
|
||||
out.errors.push_back("invalid_frame_index");
|
||||
return out;
|
||||
}
|
||||
if (!isFrameValid(frames[requestedIndex])) {
|
||||
out.errors.push_back("invalid_frame_payload");
|
||||
return out;
|
||||
}
|
||||
|
||||
out.ok = true;
|
||||
out.selectedIndex = requestedIndex;
|
||||
out.selected = frames[requestedIndex];
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<std::string> variableNames(const FrameScope& frame) {
|
||||
std::vector<std::string> names;
|
||||
for (const auto& kv : frame.variables) names.push_back(kv.first);
|
||||
return names;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool isFrameValid(const FrameScope& f) {
|
||||
return f.frameIndex >= 0 && !f.functionName.empty() &&
|
||||
!f.filePath.empty() && f.line > 0;
|
||||
}
|
||||
};
|
||||
146
editor/tests/step559_test.cpp
Normal file
146
editor/tests/step559_test.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// Step 559: Call Stack and Frame Inspector (12 tests)
|
||||
|
||||
#include "CallStackFrameInspector.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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 FrameInspectorResult& r, const std::string& e) {
|
||||
for (const auto& x : r.errors) if (x == e) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::vector<FrameScope> sampleFrames() {
|
||||
return {
|
||||
{0, "main", "main.cpp", 10, {{"argc", "2"}, {"argv", "ptr"}}},
|
||||
{1, "run", "runtime.cpp", 25, {{"cursor", "12"}, {"state", "ok"}}},
|
||||
{2, "dispatch", "runtime.cpp", 42, {{"event", "tick"}}}
|
||||
};
|
||||
}
|
||||
|
||||
void test_inspect_top_frame() {
|
||||
TEST(inspect_top_frame);
|
||||
auto r = CallStackFrameInspector::inspect(sampleFrames(), 0);
|
||||
CHECK(r.ok, "inspect should succeed");
|
||||
CHECK(r.selected.functionName == "main", "function mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_inspect_middle_frame() {
|
||||
TEST(inspect_middle_frame);
|
||||
auto r = CallStackFrameInspector::inspect(sampleFrames(), 1);
|
||||
CHECK(r.ok, "inspect should succeed");
|
||||
CHECK(r.selected.functionName == "run", "function mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_inspect_bottom_frame() {
|
||||
TEST(inspect_bottom_frame);
|
||||
auto r = CallStackFrameInspector::inspect(sampleFrames(), 2);
|
||||
CHECK(r.ok, "inspect should succeed");
|
||||
CHECK(r.selected.functionName == "dispatch", "function mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_empty_stack_fails() {
|
||||
TEST(empty_stack_fails);
|
||||
auto r = CallStackFrameInspector::inspect({}, 0);
|
||||
CHECK(!r.ok, "empty stack should fail");
|
||||
CHECK(hasError(r, "empty_stack"), "empty_stack expected");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_negative_index_fails() {
|
||||
TEST(negative_index_fails);
|
||||
auto r = CallStackFrameInspector::inspect(sampleFrames(), -1);
|
||||
CHECK(!r.ok, "negative index should fail");
|
||||
CHECK(hasError(r, "invalid_frame_index"), "invalid_frame_index expected");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_index_out_of_range_fails() {
|
||||
TEST(index_out_of_range_fails);
|
||||
auto r = CallStackFrameInspector::inspect(sampleFrames(), 5);
|
||||
CHECK(!r.ok, "out-of-range index should fail");
|
||||
CHECK(hasError(r, "invalid_frame_index"), "invalid_frame_index expected");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_invalid_frame_payload_function_fails() {
|
||||
TEST(invalid_frame_payload_function_fails);
|
||||
auto frames = sampleFrames();
|
||||
frames[1].functionName.clear();
|
||||
auto r = CallStackFrameInspector::inspect(frames, 1);
|
||||
CHECK(!r.ok, "invalid frame payload should fail");
|
||||
CHECK(hasError(r, "invalid_frame_payload"), "invalid payload expected");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_invalid_frame_payload_file_fails() {
|
||||
TEST(invalid_frame_payload_file_fails);
|
||||
auto frames = sampleFrames();
|
||||
frames[1].filePath.clear();
|
||||
auto r = CallStackFrameInspector::inspect(frames, 1);
|
||||
CHECK(!r.ok, "invalid frame payload should fail");
|
||||
CHECK(hasError(r, "invalid_frame_payload"), "invalid payload expected");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_invalid_frame_payload_line_fails() {
|
||||
TEST(invalid_frame_payload_line_fails);
|
||||
auto frames = sampleFrames();
|
||||
frames[1].line = 0;
|
||||
auto r = CallStackFrameInspector::inspect(frames, 1);
|
||||
CHECK(!r.ok, "invalid frame payload should fail");
|
||||
CHECK(hasError(r, "invalid_frame_payload"), "invalid payload expected");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_variable_scope_mapping_preserved() {
|
||||
TEST(variable_scope_mapping_preserved);
|
||||
auto r = CallStackFrameInspector::inspect(sampleFrames(), 1);
|
||||
CHECK(r.ok, "inspect should succeed");
|
||||
CHECK(r.selected.variables.at("cursor") == "12", "variable mapping mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_variable_names_exposed() {
|
||||
TEST(variable_names_exposed);
|
||||
auto r = CallStackFrameInspector::inspect(sampleFrames(), 0);
|
||||
auto names = CallStackFrameInspector::variableNames(r.selected);
|
||||
CHECK(names.size() == 2, "two names expected");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_selected_index_reported() {
|
||||
TEST(selected_index_reported);
|
||||
auto r = CallStackFrameInspector::inspect(sampleFrames(), 2);
|
||||
CHECK(r.ok, "inspect should succeed");
|
||||
CHECK(r.selectedIndex == 2, "selected index mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 559: Call Stack and Frame Inspector\n";
|
||||
|
||||
test_inspect_top_frame(); // 1
|
||||
test_inspect_middle_frame(); // 2
|
||||
test_inspect_bottom_frame(); // 3
|
||||
test_empty_stack_fails(); // 4
|
||||
test_negative_index_fails(); // 5
|
||||
test_index_out_of_range_fails(); // 6
|
||||
test_invalid_frame_payload_function_fails(); // 7
|
||||
test_invalid_frame_payload_file_fails(); // 8
|
||||
test_invalid_frame_payload_line_fails(); // 9
|
||||
test_variable_scope_mapping_preserved(); // 10
|
||||
test_variable_names_exposed(); // 11
|
||||
test_selected_index_reported(); // 12
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
33
progress.md
33
progress.md
@@ -9943,3 +9943,36 @@ capture into a complete Phase 30a debug control-plane cycle.
|
||||
- **Steps completed:** 5
|
||||
- **New tests in phase plan:** 56/56 passing
|
||||
- **Debug control-plane integration:** PASS
|
||||
|
||||
### Step 559: Call Stack and Frame Inspector
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
Implements call stack frame inspection with frame navigation and variable scope
|
||||
mapping for runtime inspection surfaces.
|
||||
|
||||
**Files added:**
|
||||
- `editor/src/CallStackFrameInspector.h` - frame inspector module:
|
||||
- frame selection by index
|
||||
- payload validation for frame navigation metadata
|
||||
- selected-frame scope extraction
|
||||
- variable name listing helper for panel rendering
|
||||
- `editor/tests/step559_test.cpp` - 12 tests covering:
|
||||
- top/middle/bottom frame navigation
|
||||
- invalid/empty stack guard behavior
|
||||
- invalid frame payload guard behavior
|
||||
- variable scope mapping and exposure behavior
|
||||
- selected-index reporting behavior
|
||||
|
||||
**Files modified:**
|
||||
- `editor/CMakeLists.txt` - `step559_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `cmake -S editor -B editor/build-native` - PASS
|
||||
- `cmake --build editor/build-native --target step559_test step558_test` - PASS
|
||||
- `./editor/build-native/step559_test` - PASS (12/12)
|
||||
- `./editor/build-native/step558_test` - PASS (8/8) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/CallStackFrameInspector.h` within header-size limit (`58` <= `600`)
|
||||
- `editor/tests/step559_test.cpp` within test-file size guidance (`146` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
Reference in New Issue
Block a user