CrossLanguageStepOverHandler, CrossLanguageStepIntoHandler, ASTNodeBreakpointMapper, BreakpointSyncOrchestrator — 26/26 tests pass. Integration step 1932: Go+TypeScript poly-API round-trip verified. Metrics: 7 whetstone calls, 5587 tokens. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
123 lines
4.5 KiB
C++
123 lines
4.5 KiB
C++
// Step 1932: Sprint 280 Integration — Poly-API cross-language step + breakpoint round-trip
|
|
//
|
|
// Scenario: a Go+TypeScript polyglot service.
|
|
// - HandleRequest is implemented in both Go and TypeScript.
|
|
// - A breakpoint is set on the HandleRequest AST node in both languages.
|
|
// - A polyglot stack trace shows the call crossing from TypeScript into Go.
|
|
// - Step-into at the boundary switches the active adapter to Go.
|
|
// - Removing the breakpoint clears both languages.
|
|
//
|
|
// t1: breakpoint fires in both languages after addToAll
|
|
// t2: step-into at TypeScript→Go boundary switches router to Go
|
|
// t3: step-over at boundary stays in TypeScript (caller side)
|
|
// t4: removeFromAll clears both Go and TypeScript breakpoints
|
|
// t5: Sprint280IntegrationSummary reports 5 steps completed
|
|
|
|
#include "BreakpointSyncOrchestrator.h"
|
|
#include "CrossLanguageStepIntoHandler.h"
|
|
#include "CrossLanguageStepOverHandler.h"
|
|
#include "DebugAdapterRouter.h"
|
|
#include "PolyglotStackTraceDecoder.h"
|
|
#include "Sprint280IntegrationSummary.h"
|
|
#include <iostream>
|
|
|
|
namespace ws = whetstone;
|
|
static int p=0,f=0;
|
|
#define T(n) { std::cout<<" "<<#n<<"... "; }
|
|
#define P() { std::cout<<"PASS\n"; ++p; }
|
|
#define F(m) { std::cout<<"FAIL: "<<m<<"\n"; ++f; }
|
|
#define C(c,m) if(!(c)){F(m);return;}
|
|
|
|
static ws::DebugAdapterRouter makeRouter() {
|
|
ws::DebugAdapterRouter r;
|
|
r.registerAdapter("TypeScript", {"stackTrace","stepIn","stepOver"});
|
|
r.registerAdapter("Go", {"stackTrace","stepIn","stepOver"});
|
|
r.setActiveLanguage("TypeScript");
|
|
return r;
|
|
}
|
|
|
|
static std::vector<ws::StackFrame> makeStack() {
|
|
ws::PolyglotStackTraceDecoder dec;
|
|
return dec.decode({
|
|
{1, "apiHandler", "api.ts", 30}, // TypeScript, boundary (next is Go)
|
|
{2, "HandleRequest", "server.go", 42}, // Go, boundary (prev is TypeScript)
|
|
{3, "processQuery", "server.go", 88}, // Go, non-boundary
|
|
});
|
|
}
|
|
|
|
void t1(){
|
|
T(breakpoint_fires_in_both_languages);
|
|
ws::BreakpointSyncOrchestrator o;
|
|
o.addToAll("HandleRequest", {
|
|
{"Go", {"server.go", 42}},
|
|
{"TypeScript", {"api.ts", 30}}
|
|
});
|
|
C(o.totalActiveBreakpoints() == 2, "two active");
|
|
auto langs = o.activeLanguages("HandleRequest");
|
|
bool hasGo = false, hasTs = false;
|
|
for (auto& l : langs) {
|
|
if (l == "Go") hasGo = true;
|
|
if (l == "TypeScript") hasTs = true;
|
|
}
|
|
C(hasGo && hasTs, "both Go and TypeScript active");
|
|
P();
|
|
}
|
|
|
|
void t2(){
|
|
T(stepInto_TypeScript_Go_boundary_switches_router);
|
|
auto router = makeRouter();
|
|
auto frames = makeStack();
|
|
ws::CrossLanguageStepIntoHandler h;
|
|
// frames[0] is TypeScript boundary; frames[1] is Go
|
|
auto r = h.stepInto(frames[0], router, frames, 0);
|
|
C( r.switched, "switched");
|
|
C( r.previousLanguage == "TypeScript", "previous TypeScript");
|
|
C( r.newLanguage == "Go", "new Go");
|
|
C( router.getActiveLanguage() == "Go", "router now Go");
|
|
C( r.targetFrameId == frames[1].id, "targetFrameId correct");
|
|
P();
|
|
}
|
|
|
|
void t3(){
|
|
T(stepOver_boundary_stays_in_TypeScript);
|
|
auto router = makeRouter();
|
|
auto frames = makeStack();
|
|
ws::CrossLanguageStepOverHandler h;
|
|
// frames[0] is TypeScript boundary — step-over stays in TypeScript
|
|
auto r = h.stepOver(frames[0], router);
|
|
C( r.crossed, "crossed");
|
|
C( r.newLanguage == "TypeScript", "stays TypeScript");
|
|
C( router.getActiveLanguage() == "TypeScript", "router still TypeScript");
|
|
P();
|
|
}
|
|
|
|
void t4(){
|
|
T(removeFromAll_clears_both_languages);
|
|
ws::BreakpointSyncOrchestrator o;
|
|
o.addToAll("HandleRequest", {
|
|
{"Go", {"server.go", 42}},
|
|
{"TypeScript", {"api.ts", 30}}
|
|
});
|
|
C(o.totalActiveBreakpoints() == 2, "two before remove");
|
|
o.removeFromAll("HandleRequest");
|
|
C(o.totalActiveBreakpoints() == 0, "zero after remove");
|
|
C(o.activeLanguages("HandleRequest").empty(), "no active languages");
|
|
P();
|
|
}
|
|
|
|
void t5(){
|
|
T(sprint280_integration_summary);
|
|
ws::Sprint280IntegrationSummary s;
|
|
C(s.stepsCompleted == 5, "5 steps");
|
|
C(s.success, "success");
|
|
C(s.sprintName() == "Sprint 280: Cross-Language Step + Breakpoints", "name");
|
|
P();
|
|
}
|
|
|
|
int main(){
|
|
std::cout << "Step 1932: Sprint 280 Integration\n";
|
|
t1(); t2(); t3(); t4(); t5();
|
|
std::cout << "\n" << p << "/" << (p+f) << " passed\n";
|
|
return f > 0 ? 1 : 0;
|
|
}
|