@@ -36,14 +36,14 @@ type testEnvironment struct {
36
36
ws * fake.Workspace
37
37
ts * servertest.Server
38
38
39
- diagnostics <- chan * protocol. PublishDiagnosticsParams
39
+ dw diagnosticsWatcher
40
40
}
41
41
42
- func setupEnv (t * testing.T ) (context.Context , testEnvironment , func ()) {
42
+ func setupEnv (t * testing.T , txt string ) (context.Context , testEnvironment , func ()) {
43
43
t .Helper ()
44
44
ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
45
45
46
- ws , err := fake .NewWorkspace ("get-diagnostics" , []byte (exampleProgram ))
46
+ ws , err := fake .NewWorkspace ("get-diagnostics" , []byte (txt ))
47
47
if err != nil {
48
48
t .Fatal (err )
49
49
}
@@ -55,24 +55,63 @@ func setupEnv(t *testing.T) (context.Context, testEnvironment, func()) {
55
55
if err != nil {
56
56
t .Fatal (err )
57
57
}
58
- diags := make (chan * protocol.PublishDiagnosticsParams , 10 )
59
- editor .Client ().OnDiagnostics (func (_ context.Context , params * protocol.PublishDiagnosticsParams ) error {
60
- diags <- params
61
- return nil
62
- })
58
+ dw := newDiagWatcher (ws )
59
+ editor .Client ().OnDiagnostics (dw .onDiagnostics )
63
60
cleanup := func () {
64
61
cancel ()
65
62
ts .Close ()
66
63
ws .Close ()
67
64
}
68
65
return ctx , testEnvironment {
69
- editor : editor ,
70
- ws : ws ,
71
- ts : ts ,
72
- diagnostics : diags ,
66
+ editor : editor ,
67
+ ws : ws ,
68
+ ts : ts ,
69
+ dw : dw ,
73
70
}, cleanup
74
71
}
75
72
73
+ type diagnosticsWatcher struct {
74
+ diagnostics chan * protocol.PublishDiagnosticsParams
75
+ ws * fake.Workspace
76
+ }
77
+
78
+ func newDiagWatcher (ws * fake.Workspace ) diagnosticsWatcher {
79
+ return diagnosticsWatcher {
80
+ // Allow an arbitrarily large buffer, as we should never want onDiagnostics
81
+ // to block.
82
+ diagnostics : make (chan * protocol.PublishDiagnosticsParams , 1000 ),
83
+ ws : ws ,
84
+ }
85
+ }
86
+
87
+ func (w diagnosticsWatcher ) onDiagnostics (_ context.Context , p * protocol.PublishDiagnosticsParams ) error {
88
+ w .diagnostics <- p
89
+ return nil
90
+ }
91
+
92
+ func (w diagnosticsWatcher ) await (ctx context.Context , expected ... string ) (map [string ]* protocol.PublishDiagnosticsParams , error ) {
93
+ expectedSet := make (map [string ]bool )
94
+ for _ , e := range expected {
95
+ expectedSet [e ] = true
96
+ }
97
+ got := make (map [string ]* protocol.PublishDiagnosticsParams )
98
+ for len (got ) < len (expectedSet ) {
99
+ select {
100
+ case <- ctx .Done ():
101
+ return nil , ctx .Err ()
102
+ case d := <- w .diagnostics :
103
+ pth , err := w .ws .URIToPath (d .URI )
104
+ if err != nil {
105
+ return nil , err
106
+ }
107
+ if expectedSet [pth ] {
108
+ got [pth ] = d
109
+ }
110
+ }
111
+ }
112
+ return got , nil
113
+ }
114
+
76
115
func checkDiagnosticLocation (params * protocol.PublishDiagnosticsParams , filename string , line , col int ) error {
77
116
if got , want := params .URI , filename ; got != want {
78
117
return fmt .Errorf ("got diagnostics for URI %q, want %q" , got , want )
@@ -89,7 +128,7 @@ func checkDiagnosticLocation(params *protocol.PublishDiagnosticsParams, filename
89
128
90
129
func TestDiagnosticErrorInEditedFile (t * testing.T ) {
91
130
t .Parallel ()
92
- ctx , env , cleanup := setupEnv (t )
131
+ ctx , env , cleanup := setupEnv (t , exampleProgram )
93
132
defer cleanup ()
94
133
95
134
// Deleting the 'n' at the end of Println should generate a single error
@@ -107,15 +146,18 @@ func TestDiagnosticErrorInEditedFile(t *testing.T) {
107
146
if err := env .editor .EditBuffer (ctx , "main.go" , edits ); err != nil {
108
147
t .Fatal (err )
109
148
}
110
- params := awaitDiagnostics (ctx , t , env .diagnostics )
111
- if err := checkDiagnosticLocation (params , env .ws .URI ("main.go" ), 5 , 5 ); err != nil {
149
+ diags , err := env .dw .await (ctx , "main.go" )
150
+ if err != nil {
151
+ t .Fatal (err )
152
+ }
153
+ if err := checkDiagnosticLocation (diags ["main.go" ], env .ws .URI ("main.go" ), 5 , 5 ); err != nil {
112
154
t .Fatal (err )
113
155
}
114
156
}
115
157
116
158
func TestSimultaneousEdits (t * testing.T ) {
117
159
t .Parallel ()
118
- ctx , env , cleanup := setupEnv (t )
160
+ ctx , env , cleanup := setupEnv (t , exampleProgram )
119
161
defer cleanup ()
120
162
121
163
// Set up a second editor session connected to the same server, using the
@@ -125,12 +167,8 @@ func TestSimultaneousEdits(t *testing.T) {
125
167
if err != nil {
126
168
t .Fatal (err )
127
169
}
128
- diags2 := make (chan * protocol.PublishDiagnosticsParams , 10 )
129
- editor2 .Client ().OnDiagnostics (func (_ context.Context , params * protocol.PublishDiagnosticsParams ) error {
130
- diags2 <- params
131
- return nil
132
- })
133
-
170
+ dw2 := newDiagWatcher (env .ws )
171
+ editor2 .Client ().OnDiagnostics (dw2 .onDiagnostics )
134
172
// In editor #1, break fmt.Println as before.
135
173
edits1 := []fake.Edit {{
136
174
Start : fake.Pos {Line : 5 , Column : 11 },
@@ -156,23 +194,19 @@ func TestSimultaneousEdits(t *testing.T) {
156
194
if err := editor2 .EditBuffer (ctx , "main.go" , edits2 ); err != nil {
157
195
t .Fatal (err )
158
196
}
159
- params1 := awaitDiagnostics (ctx , t , env .diagnostics )
160
- params2 := awaitDiagnostics (ctx , t , diags2 )
161
- if err := checkDiagnosticLocation (params1 , env .ws .URI ("main.go" ), 5 , 5 ); err != nil {
197
+ diags1 , err := env .dw .await (ctx , "main.go" )
198
+ if err != nil {
162
199
t .Fatal (err )
163
200
}
164
- if err := checkDiagnosticLocation (params2 , env .ws .URI ("main.go" ), 7 , 0 ); err != nil {
201
+ diags2 , err := dw2 .await (ctx , "main.go" )
202
+ if err != nil {
165
203
t .Fatal (err )
166
204
}
167
- }
168
-
169
- func awaitDiagnostics (ctx context.Context , t * testing.T , diags <- chan * protocol.PublishDiagnosticsParams ) * protocol.PublishDiagnosticsParams {
170
- t .Helper ()
171
- select {
172
- case <- ctx .Done ():
173
- panic (ctx .Err ())
174
- case d := <- diags :
175
- return d
205
+ if err := checkDiagnosticLocation (diags1 ["main.go" ], env .ws .URI ("main.go" ), 5 , 5 ); err != nil {
206
+ t .Fatal (err )
207
+ }
208
+ if err := checkDiagnosticLocation (diags2 ["main.go" ], env .ws .URI ("main.go" ), 7 , 0 ); err != nil {
209
+ t .Fatal (err )
176
210
}
177
211
}
178
212
@@ -183,14 +217,91 @@ const Foo = "abc
183
217
184
218
func TestDiagnosticErrorInNewFile (t * testing.T ) {
185
219
t .Parallel ()
186
- ctx , env , cleanup := setupEnv (t )
220
+ ctx , env , cleanup := setupEnv (t , exampleProgram )
187
221
defer cleanup ()
188
222
189
223
if err := env .editor .CreateBuffer (ctx , "broken.go" , brokenFile ); err != nil {
190
224
t .Fatal (err )
191
225
}
192
- params := awaitDiagnostics (ctx , t , env .diagnostics )
193
- if got , want := params .URI , env .ws .URI ("broken.go" ); got != want {
194
- t .Fatalf ("got diagnostics for URI %q, want %q" , got , want )
226
+ _ , err := env .dw .await (ctx , "broken.go" )
227
+ if err != nil {
228
+ t .Fatal (err )
229
+ }
230
+ }
231
+
232
+ // badPackage contains a duplicate definition of the 'a' const.
233
+ const badPackage = `
234
+ -- go.mod --
235
+ module mod
236
+
237
+ go 1.12
238
+ -- a.go --
239
+ package consts
240
+
241
+ const a = 1
242
+ -- b.go --
243
+ package consts
244
+
245
+ const a = 2
246
+ `
247
+
248
+ func TestDiagnosticClearingOnEdit (t * testing.T ) {
249
+ t .Parallel ()
250
+ ctx , env , cleanup := setupEnv (t , badPackage )
251
+ defer cleanup ()
252
+
253
+ if err := env .editor .OpenFile (ctx , "b.go" ); err != nil {
254
+ t .Fatal (err )
255
+ }
256
+ _ , err := env .dw .await (ctx , "a.go" , "b.go" )
257
+ if err != nil {
258
+ t .Fatal (err )
259
+ }
260
+
261
+ // In editor #2 remove the closing brace.
262
+ edits := []fake.Edit {{
263
+ Start : fake.Pos {Line : 2 , Column : 6 },
264
+ End : fake.Pos {Line : 2 , Column : 7 },
265
+ Text : "b" ,
266
+ }}
267
+ if err := env .editor .EditBuffer (ctx , "b.go" , edits ); err != nil {
268
+ t .Fatal (err )
269
+ }
270
+ diags , err := env .dw .await (ctx , "a.go" , "b.go" )
271
+ if err != nil {
272
+ t .Fatal (err )
273
+ }
274
+ for pth , d := range diags {
275
+ if len (d .Diagnostics ) != 0 {
276
+ t .Errorf ("non-empty diagnostics for %q" , pth )
277
+ }
278
+ }
279
+ }
280
+
281
+ func TestDiagnosticClearingOnDelete (t * testing.T ) {
282
+ t .Skip ("skipping due to golang.org/issues/37049" )
283
+
284
+ t .Parallel ()
285
+ ctx , env , cleanup := setupEnv (t , badPackage )
286
+ defer cleanup ()
287
+
288
+ if err := env .editor .OpenFile (ctx , "a.go" ); err != nil {
289
+ t .Fatal (err )
290
+ }
291
+ _ , err := env .dw .await (ctx , "a.go" , "b.go" )
292
+ if err != nil {
293
+ t .Fatal (err )
294
+ }
295
+ env .ws .RemoveFile (ctx , "b.go" )
296
+
297
+ // TODO(golang.org/issues/37049): here we only get diagnostics for a.go.
298
+ diags , err := env .dw .await (ctx , "a.go" , "b.go" )
299
+ if err != nil {
300
+ t .Fatal (err )
301
+ }
302
+ for pth , d := range diags {
303
+ if len (d .Diagnostics ) != 0 {
304
+ t .Errorf ("non-empty diagnostics for %q" , pth )
305
+ }
195
306
}
196
307
}
0 commit comments