Skip to content

Commit 1a0d0bb

Browse files
committed
Convert SteppingTest to BDD style for proper beforeEach support
1 parent bb88511 commit 1a0d0bb

File tree

1 file changed

+137
-140
lines changed

1 file changed

+137
-140
lines changed

test/cfml/SteppingTest.cfc

Lines changed: 137 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="dap" {
88
variables.targetFile = "";
99

1010
// Line numbers in stepping-target.cfm - keep in sync with the file
11-
// These are validated in testValidateLineNumbers() using breakpointLocations (native mode only)
1211
variables.lines = {
1312
innerFuncBody: 9, // var doubled = arguments.x * 2;
1413
innerFuncReturn: 10, // return doubled;
@@ -21,202 +20,200 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="dap" {
2120
mainSecondOutput: 28 // writeOutput( " Second: #secondResult#" );
2221
};
2322

24-
function beforeAll() {
25-
// Set up paths only - DAP connection happens per-test to ensure isolation
26-
variables.dapHost = server.system.environment.DAP_HOST ?: "localhost";
27-
variables.dapPort = val( server.system.environment.DAP_PORT ?: 10000 );
28-
variables.debuggeeHttp = server.system.environment.DEBUGGEE_HTTP ?: "http://localhost:8888";
29-
variables.debuggeeArtifactPath = server.system.environment.DEBUGGEE_ARTIFACT_PATH ?: "";
30-
variables.dapSecret = server.system.environment.DAP_SECRET ?: "testing";
23+
function run( testResults, testBox ) {
3124
variables.targetFile = getArtifactPath( "stepping-target.cfm" );
32-
}
3325

34-
function beforeEach() {
35-
// Fresh DAP connection for each test - disconnect clears any stuck threads via continueAll()
36-
setupDap();
37-
}
26+
describe( "Stepping Tests", function() {
3827

39-
// Validate our line number assumptions using breakpointLocations (native mode only)
40-
function testValidateLineNumbers_steppingTarget() skip="notSupportsBreakpointLocations" {
41-
var locations = dap.breakpointLocations( variables.targetFile, 1, 35 );
42-
var validLines = locations.body.breakpoints.map( function( bp ) { return bp.line; } );
28+
beforeEach( function() {
29+
// Fresh DAP connection for each test - disconnect triggers continueAll() on server
30+
setupDap();
31+
} );
4332

44-
systemOutput( "#variables.targetFile# valid lines: #serializeJSON( validLines )#", true );
33+
afterEach( function() {
34+
clearBreakpoints( variables.targetFile );
35+
teardownDap();
36+
} );
4537

46-
for ( var key in variables.lines ) {
47-
var line = variables.lines[ key ];
48-
expect( validLines ).toInclude( line, "#variables.targetFile# line #line# (#key#) should be a valid breakpoint location" );
49-
}
50-
}
38+
it( "validates line numbers in stepping-target", function() {
39+
if ( notSupportsBreakpointLocations() ) return skip();
5140

52-
function afterEach() {
53-
clearBreakpoints( variables.targetFile );
54-
// Disconnect after each test - this triggers continueAll() on server, resuming stuck threads
55-
teardownDap();
56-
}
41+
var locations = dap.breakpointLocations( variables.targetFile, 1, 35 );
42+
var validLines = locations.body.breakpoints.map( function( bp ) { return bp.line; } );
5743

58-
// ========== Step Over ==========
44+
systemOutput( "#variables.targetFile# valid lines: #serializeJSON( validLines )#", true );
5945

60-
function testStepOverSkipsFunction() {
61-
// Set breakpoint at call to outerFunc
62-
dap.setBreakpoints( variables.targetFile, [ lines.mainCallOuter ] );
46+
for ( var key in variables.lines ) {
47+
var line = variables.lines[ key ];
48+
expect( validLines ).toInclude( line, "#variables.targetFile# line #line# (#key#) should be a valid breakpoint location" );
49+
}
50+
} );
6351

64-
triggerArtifact( "stepping-target.cfm" );
52+
// ========== Step Over ==========
6553

66-
var stopped = dap.waitForEvent( "stopped", 2000 );
67-
var threadId = stopped.body.threadId;
54+
it( "stepOver skips function call", function() {
55+
// Set breakpoint at call to outerFunc
56+
dap.setBreakpoints( variables.targetFile, [ lines.mainCallOuter ] );
6857

69-
// Verify we're at mainCallOuter
70-
var frame = getTopFrame( threadId );
71-
expect( frame.line ).toBe( lines.mainCallOuter, "Should start at mainCallOuter" );
58+
triggerArtifact( "stepping-target.cfm" );
7259

73-
// Step over - should skip into outerFunc and land on mainWriteOutput
74-
dap.stepOver( threadId );
75-
stopped = dap.waitForEvent( "stopped", 2000 );
60+
var stopped = dap.waitForEvent( "stopped", 2000 );
61+
var threadId = stopped.body.threadId;
7662

77-
frame = getTopFrame( threadId );
78-
expect( frame.line ).toBe( lines.mainWriteOutput, "Step over should land on mainWriteOutput" );
63+
// Verify we're at mainCallOuter
64+
var frame = getTopFrame( threadId );
65+
expect( frame.line ).toBe( lines.mainCallOuter, "Should start at mainCallOuter" );
7966

80-
cleanupThread( threadId );
81-
}
67+
// Step over - should skip into outerFunc and land on mainWriteOutput
68+
dap.stepOver( threadId );
69+
stopped = dap.waitForEvent( "stopped", 2000 );
8270

83-
// ========== Step In ==========
71+
frame = getTopFrame( threadId );
72+
expect( frame.line ).toBe( lines.mainWriteOutput, "Step over should land on mainWriteOutput" );
8473

85-
function testStepInEntersFunction() {
86-
// Set breakpoint at call to outerFunc
87-
dap.setBreakpoints( variables.targetFile, [ lines.mainCallOuter ] );
74+
cleanupThread( threadId );
75+
} );
8876

89-
triggerArtifact( "stepping-target.cfm" );
77+
// ========== Step In ==========
9078

91-
var stopped = dap.waitForEvent( "stopped", 2000 );
92-
var threadId = stopped.body.threadId;
79+
it( "stepIn enters function", function() {
80+
// Set breakpoint at call to outerFunc
81+
dap.setBreakpoints( variables.targetFile, [ lines.mainCallOuter ] );
9382

94-
// Verify we're at mainCallOuter
95-
var frame = getTopFrame( threadId );
96-
expect( frame.line ).toBe( lines.mainCallOuter, "Should start at mainCallOuter" );
83+
triggerArtifact( "stepping-target.cfm" );
9784

98-
// Step in - should enter outerFunc at outerFuncBody
99-
dap.stepIn( threadId );
100-
stopped = dap.waitForEvent( "stopped", 2000 );
85+
var stopped = dap.waitForEvent( "stopped", 2000 );
86+
var threadId = stopped.body.threadId;
10187

102-
frame = getTopFrame( threadId );
103-
expect( frame.line ).toBe( lines.outerFuncBody, "Step in should enter outerFunc at outerFuncBody" );
88+
// Verify we're at mainCallOuter
89+
var frame = getTopFrame( threadId );
90+
expect( frame.line ).toBe( lines.mainCallOuter, "Should start at mainCallOuter" );
10491

105-
cleanupThread( threadId );
106-
}
92+
// Step in - should enter outerFunc at outerFuncBody
93+
dap.stepIn( threadId );
94+
stopped = dap.waitForEvent( "stopped", 2000 );
10795

108-
function testStepInNestedFunction() {
109-
// Set breakpoint at call to outerFunc
110-
dap.setBreakpoints( variables.targetFile, [ lines.mainCallOuter ] );
96+
frame = getTopFrame( threadId );
97+
expect( frame.line ).toBe( lines.outerFuncBody, "Step in should enter outerFunc at outerFuncBody" );
11198

112-
triggerArtifact( "stepping-target.cfm" );
99+
cleanupThread( threadId );
100+
} );
113101

114-
var stopped = dap.waitForEvent( "stopped", 2000 );
115-
var threadId = stopped.body.threadId;
102+
it( "stepIn enters nested function", function() {
103+
// Set breakpoint at call to outerFunc
104+
dap.setBreakpoints( variables.targetFile, [ lines.mainCallOuter ] );
116105

117-
// Step into outerFunc
118-
dap.stepIn( threadId );
119-
stopped = dap.waitForEvent( "stopped", 2000 );
106+
triggerArtifact( "stepping-target.cfm" );
120107

121-
var frame = getTopFrame( threadId );
122-
expect( frame.line ).toBe( lines.outerFuncBody, "Should be at outerFuncBody" );
108+
var stopped = dap.waitForEvent( "stopped", 2000 );
109+
var threadId = stopped.body.threadId;
123110

124-
// Step over to outerFuncCallInner (call to innerFunc)
125-
dap.stepOver( threadId );
126-
stopped = dap.waitForEvent( "stopped", 2000 );
111+
// Step into outerFunc
112+
dap.stepIn( threadId );
113+
stopped = dap.waitForEvent( "stopped", 2000 );
127114

128-
frame = getTopFrame( threadId );
129-
expect( frame.line ).toBe( lines.outerFuncCallInner, "Should be at outerFuncCallInner" );
115+
var frame = getTopFrame( threadId );
116+
expect( frame.line ).toBe( lines.outerFuncBody, "Should be at outerFuncBody" );
130117

131-
// Step into innerFunc
132-
dap.stepIn( threadId );
133-
stopped = dap.waitForEvent( "stopped", 2000 );
118+
// Step over to outerFuncCallInner (call to innerFunc)
119+
dap.stepOver( threadId );
120+
stopped = dap.waitForEvent( "stopped", 2000 );
134121

135-
frame = getTopFrame( threadId );
136-
expect( frame.line ).toBe( lines.innerFuncBody, "Step in should enter innerFunc at innerFuncBody" );
122+
frame = getTopFrame( threadId );
123+
expect( frame.line ).toBe( lines.outerFuncCallInner, "Should be at outerFuncCallInner" );
137124

138-
cleanupThread( threadId );
139-
}
125+
// Step into innerFunc
126+
dap.stepIn( threadId );
127+
stopped = dap.waitForEvent( "stopped", 2000 );
140128

141-
// ========== Step Out ==========
129+
frame = getTopFrame( threadId );
130+
expect( frame.line ).toBe( lines.innerFuncBody, "Step in should enter innerFunc at innerFuncBody" );
142131

143-
function testStepOutExitsFunction() {
144-
// Set breakpoint at call to outerFunc
145-
dap.setBreakpoints( variables.targetFile, [ lines.mainCallOuter ] );
132+
cleanupThread( threadId );
133+
} );
146134

147-
triggerArtifact( "stepping-target.cfm" );
135+
// ========== Step Out ==========
148136

149-
var stopped = dap.waitForEvent( "stopped", 2000 );
150-
var threadId = stopped.body.threadId;
137+
it( "stepOut exits function", function() {
138+
// Set breakpoint at call to outerFunc
139+
dap.setBreakpoints( variables.targetFile, [ lines.mainCallOuter ] );
151140

152-
// Step into outerFunc
153-
dap.stepIn( threadId );
154-
stopped = dap.waitForEvent( "stopped", 2000 );
141+
triggerArtifact( "stepping-target.cfm" );
155142

156-
var frame = getTopFrame( threadId );
157-
expect( frame.line ).toBe( lines.outerFuncBody, "Should be inside outerFunc" );
143+
var stopped = dap.waitForEvent( "stopped", 2000 );
144+
var threadId = stopped.body.threadId;
158145

159-
// Step out - should return to caller at mainWriteOutput
160-
dap.stepOut( threadId );
161-
stopped = dap.waitForEvent( "stopped", 2000 );
146+
// Step into outerFunc
147+
dap.stepIn( threadId );
148+
stopped = dap.waitForEvent( "stopped", 2000 );
162149

163-
frame = getTopFrame( threadId );
164-
expect( frame.line ).toBe( lines.mainWriteOutput, "Step out should return to mainWriteOutput" );
150+
var frame = getTopFrame( threadId );
151+
expect( frame.line ).toBe( lines.outerFuncBody, "Should be inside outerFunc" );
165152

166-
cleanupThread( threadId );
167-
}
153+
// Step out - should return to caller at mainWriteOutput
154+
dap.stepOut( threadId );
155+
stopped = dap.waitForEvent( "stopped", 2000 );
168156

169-
function testStepOutFromNestedFunction() {
170-
// Set breakpoint inside innerFunc
171-
dap.setBreakpoints( variables.targetFile, [ lines.innerFuncBody ] );
157+
frame = getTopFrame( threadId );
158+
expect( frame.line ).toBe( lines.mainWriteOutput, "Step out should return to mainWriteOutput" );
172159

173-
triggerArtifact( "stepping-target.cfm" );
160+
cleanupThread( threadId );
161+
} );
174162

175-
var stopped = dap.waitForEvent( "stopped", 2000 );
176-
var threadId = stopped.body.threadId;
163+
it( "stepOut from nested function returns to caller", function() {
164+
// Set breakpoint inside innerFunc
165+
dap.setBreakpoints( variables.targetFile, [ lines.innerFuncBody ] );
177166

178-
// Verify we're in innerFunc
179-
var frame = getTopFrame( threadId );
180-
expect( frame.line ).toBe( lines.innerFuncBody, "Should be at innerFuncBody" );
167+
triggerArtifact( "stepping-target.cfm" );
181168

182-
// Step out - should return to outerFunc at outerFuncReturn
183-
dap.stepOut( threadId );
184-
stopped = dap.waitForEvent( "stopped", 2000 );
169+
var stopped = dap.waitForEvent( "stopped", 2000 );
170+
var threadId = stopped.body.threadId;
185171

186-
frame = getTopFrame( threadId );
187-
expect( frame.line ).toBe( lines.outerFuncReturn, "Step out should return to outerFuncReturn" );
172+
// Verify we're in innerFunc
173+
var frame = getTopFrame( threadId );
174+
expect( frame.line ).toBe( lines.innerFuncBody, "Should be at innerFuncBody" );
188175

189-
cleanupThread( threadId );
190-
}
176+
// Step out - should return to outerFunc at outerFuncReturn
177+
dap.stepOut( threadId );
178+
stopped = dap.waitForEvent( "stopped", 2000 );
179+
180+
frame = getTopFrame( threadId );
181+
expect( frame.line ).toBe( lines.outerFuncReturn, "Step out should return to outerFuncReturn" );
182+
183+
cleanupThread( threadId );
184+
} );
185+
186+
// ========== Stack Trace ==========
191187

192-
// ========== Stack Trace ==========
188+
it( "stack trace shows call hierarchy", function() {
189+
// Set breakpoint inside innerFunc
190+
dap.setBreakpoints( variables.targetFile, [ lines.innerFuncBody ] );
193191

194-
function testStackTraceShowsCallHierarchy() {
195-
// Set breakpoint inside innerFunc
196-
dap.setBreakpoints( variables.targetFile, [ lines.innerFuncBody ] );
192+
triggerArtifact( "stepping-target.cfm" );
197193

198-
triggerArtifact( "stepping-target.cfm" );
194+
var stopped = dap.waitForEvent( "stopped", 2000 );
195+
var threadId = stopped.body.threadId;
199196

200-
var stopped = dap.waitForEvent( "stopped", 2000 );
201-
var threadId = stopped.body.threadId;
197+
// Get full stack trace
198+
var stackResponse = dap.stackTrace( threadId );
199+
var frames = stackResponse.body.stackFrames;
202200

203-
// Get full stack trace
204-
var stackResponse = dap.stackTrace( threadId );
205-
var frames = stackResponse.body.stackFrames;
201+
// Should have at least 2 frames: innerFunc -> outerFunc
202+
// Note: Native mode only shows UDF frames, not the top-level "main" frame
203+
// Agent mode may show 3 frames including top-level code
204+
expect( frames.len() ).toBeGTE( 2, "Should have at least 2 stack frames, got #serializeJSON( frames )#" );
206205

207-
// Should have at least 2 frames: innerFunc -> outerFunc
208-
// Note: Native mode only shows UDF frames, not the top-level "main" frame
209-
// Agent mode may show 3 frames including top-level code
210-
expect( frames.len() ).toBeGTE( 2, "Should have at least 2 stack frames, got #serializeJSON(frames)#" );
206+
// Top frame should be innerFunc
207+
expect( frames[ 1 ].name ).toInclude( "innerFunc" );
208+
expect( frames[ 1 ].line ).toBe( lines.innerFuncBody );
211209

212-
// Top frame should be innerFunc
213-
expect( frames[ 1 ].name ).toInclude( "innerFunc" );
214-
expect( frames[ 1 ].line ).toBe( lines.innerFuncBody );
210+
// Second frame should be outerFunc
211+
expect( frames[ 2 ].name ).toInclude( "outerFunc" );
215212

216-
// Second frame should be outerFunc
217-
expect( frames[ 2 ].name ).toInclude( "outerFunc" );
213+
cleanupThread( threadId );
214+
} );
218215

219-
cleanupThread( threadId );
216+
} );
220217
}
221218

222219
}

0 commit comments

Comments
 (0)