@@ -3,13 +3,15 @@ package dap
33import (
44 "context"
55 "encoding/json"
6+ "fmt"
67 "io"
78 "path/filepath"
89 "testing"
910 "time"
1011
1112 "github.com/docker/buildx/dap/common"
1213 "github.com/google/go-dap"
14+ "github.com/moby/buildkit/solver/pb"
1315 "github.com/stretchr/testify/assert"
1416 "golang.org/x/sync/errgroup"
1517)
@@ -134,6 +136,104 @@ func TestSetBreakpoints(t *testing.T) {
134136 eg .Wait ()
135137}
136138
139+ func TestBreakpointMapIntersectVerified (t * testing.T ) {
140+ t .Parallel ()
141+
142+ ws := t .TempDir ()
143+ filename := "Dockerfile"
144+ fpath := filepath .Join (ws , filename )
145+
146+ type breakpointCase struct {
147+ desc string
148+ sbp dap.SourceBreakpoint
149+ expectVerified bool
150+ }
151+
152+ docRanges := []* pb.Range {
153+ {Start : & pb.Position {Line : 10 , Character : 0 }, End : & pb.Position {Line : 10 , Character : 10 }},
154+ {Start : & pb.Position {Line : 20 , Character : 5 }, End : & pb.Position {Line : 20 , Character : 5 }},
155+ {Start : & pb.Position {Line : 30 , Character : 0 }, End : & pb.Position {Line : 30 , Character : 10 }},
156+ {Start : & pb.Position {Line : 35 , Character : 2 }, End : & pb.Position {Line : 35 , Character : 7 }},
157+ }
158+
159+ breakpointCases := []breakpointCase {
160+ {desc : "inside range 0" , sbp : dap.SourceBreakpoint {Line : 10 , Column : 5 }, expectVerified : true },
161+ {desc : "before range 0" , sbp : dap.SourceBreakpoint {Line : 10 , Column : - 1 }},
162+ {desc : "range 1 point" , sbp : dap.SourceBreakpoint {Line : 20 , Column : 5 }, expectVerified : true },
163+ {desc : "before range 1 point" , sbp : dap.SourceBreakpoint {Line : 20 , Column : 4 }},
164+ {desc : "range 2 end" , sbp : dap.SourceBreakpoint {Line : 30 , Column : 10 }, expectVerified : true },
165+ {desc : "after range 2" , sbp : dap.SourceBreakpoint {Line : 30 , Column : 11 }},
166+ {desc : "inside range 3" , sbp : dap.SourceBreakpoint {Line : 35 , Column : 4 }, expectVerified : true },
167+ {desc : "different line" , sbp : dap.SourceBreakpoint {Line : 40 , Column : 0 }},
168+ }
169+
170+ bm := newBreakpointMap ()
171+ sbps := make ([]dap.SourceBreakpoint , len (breakpointCases ))
172+ for i , bc := range breakpointCases {
173+ sbps [i ] = bc .sbp
174+ }
175+ bm .Set (fpath , sbps )
176+
177+ srcLocs := make (map [string ]* pb.Locations , len (docRanges ))
178+ for i , rng := range docRanges {
179+ srcLocs [fmt .Sprintf ("doc-%d" , i )] = & pb.Locations {
180+ Locations : []* pb.Location {{
181+ SourceIndex : 0 ,
182+ Ranges : []* pb.Range {rng },
183+ }},
184+ }
185+ }
186+
187+ src := & pb.Source {
188+ Locations : srcLocs ,
189+ Infos : []* pb.SourceInfo {
190+ {Filename : filename },
191+ },
192+ }
193+
194+ ctx := newBreakpointTestContext (t )
195+ digests := bm .Intersect (ctx , src , ws )
196+ wantMatches := 0
197+ for _ , bc := range breakpointCases {
198+ if bc .expectVerified {
199+ wantMatches ++
200+ }
201+ }
202+ assert .Len (t , digests , wantMatches )
203+
204+ expectedEvents := make (map [int ]struct {})
205+ for i , bp := range bm .byPath [fpath ] {
206+ if breakpointCases [i ].expectVerified {
207+ expectedEvents [bp .Id ] = struct {}{}
208+ }
209+ }
210+
211+ for len (expectedEvents ) > 0 {
212+ select {
213+ case msg := <- ctx .messages :
214+ evt , ok := msg .(* dap.BreakpointEvent )
215+ if ! assert .True (t , ok , "expected breakpoint event message" ) {
216+ continue
217+ }
218+ if _ , ok := expectedEvents [evt .Body .Breakpoint .Id ]; ok {
219+ delete (expectedEvents , evt .Body .Breakpoint .Id )
220+ assert .True (t , evt .Body .Breakpoint .Verified )
221+ } else {
222+ t .Fatalf ("unexpected breakpoint event for id %d" , evt .Body .Breakpoint .Id )
223+ }
224+ case <- time .After (time .Second ):
225+ t .Fatalf ("expected %d more breakpoint events" , len (expectedEvents ))
226+ }
227+ }
228+
229+ stored := bm .byPath [fpath ]
230+ if assert .Len (t , stored , len (breakpointCases )) {
231+ for i , bc := range breakpointCases {
232+ assert .Equal (t , bc .expectVerified , stored [i ].Verified , "breakpoint %d (%s) mismatch" , i , bc .desc )
233+ }
234+ }
235+ }
236+
137237func NewTestAdapter [C LaunchConfig ](t * testing.T ) (* Adapter [C ], Conn , * Client ) {
138238 t .Helper ()
139239
@@ -193,3 +293,29 @@ func (c *loggingConn) RecvMsg(ctx context.Context) (dap.Message, error) {
193293 c .t .Logf ("[%s] recv: %v" , c .prefix , string (b ))
194294 return m , nil
195295}
296+
297+ type breakpointTestContext struct {
298+ context.Context
299+ messages chan dap.Message
300+ }
301+
302+ func newBreakpointTestContext (t * testing.T ) * breakpointTestContext {
303+ t .Helper ()
304+ return & breakpointTestContext {
305+ Context : context .Background (),
306+ messages : make (chan dap.Message , 16 ),
307+ }
308+ }
309+
310+ func (c * breakpointTestContext ) C () chan <- dap.Message {
311+ return c .messages
312+ }
313+
314+ func (c * breakpointTestContext ) Go (f func (Context )) bool {
315+ go f (c )
316+ return true
317+ }
318+
319+ func (c * breakpointTestContext ) Request (dap.RequestMessage ) dap.ResponseMessage {
320+ return nil
321+ }
0 commit comments