@@ -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