@@ -32,87 +32,18 @@ const A = 1
32
32
// EOF
33
33
`
34
34
Run (t , files , func (t * testing.T , env * Env ) {
35
- env .OpenFile ("a/a.go" )
36
-
37
- // Invoke the "View package documentation" code
38
- // action to start the server.
39
- var docAction * protocol.CodeAction
40
- actions := env .CodeAction ("a/a.go" , nil )
41
- for _ , act := range actions {
42
- if act .Title == "View package documentation" {
43
- docAction = & act
44
- break
45
- }
46
- }
47
- if docAction == nil {
48
- t .Fatalf ("can't find action with Title 'View package documentation', only %#v" ,
49
- actions )
50
- }
51
-
52
- // Execute the command.
53
- // Its side effect should be a single showDocument request.
54
- params := & protocol.ExecuteCommandParams {
55
- Command : docAction .Command .Command ,
56
- Arguments : docAction .Command .Arguments ,
57
- }
58
- var result command.DebuggingResult
59
- env .ExecuteCommand (params , & result )
60
-
61
- // shownDocument returns the first shown document matching the URI prefix.
62
- //
63
- // TODO(adonovan): the integration test framework
64
- // needs a way to reset ShownDocuments so they don't
65
- // accumulate, necessitating the fragile prefix hack.
66
- shownDocument := func (prefix string ) * protocol.ShowDocumentParams {
67
- var shown []* protocol.ShowDocumentParams
68
- env .Await (ShownDocuments (& shown ))
69
- var first * protocol.ShowDocumentParams
70
- for _ , sd := range shown {
71
- if strings .HasPrefix (sd .URI , prefix ) {
72
- if first != nil {
73
- t .Errorf ("got multiple showDocument requests: %#v" , shown )
74
- break
75
- }
76
- first = sd
77
- }
78
- }
79
- return first
80
- }
81
-
82
- // get fetches the content of a document over HTTP.
83
- get := func (url string ) []byte {
84
- resp , err := http .Get (url )
85
- if err != nil {
86
- t .Fatal (err )
87
- }
88
- defer resp .Body .Close ()
89
- got , err := io .ReadAll (resp .Body )
90
- if err != nil {
91
- t .Fatal (err )
92
- }
93
- return got
94
- }
95
-
96
- checkMatch := func (got []byte , pattern string ) {
97
- if ! regexp .MustCompile (pattern ).Match (got ) {
98
- t .Errorf ("input did not match pattern %q; got:\n %s" ,
99
- pattern , got )
100
- }
101
- }
102
-
103
35
// Assert that the HTML page contains the expected const declaration.
104
36
// (We may need to make allowances for HTML markup.)
105
- shownDoc := shownDocument ("http:" )
106
- t .Log ("showDocument(package doc) URL:" , shownDoc .URI )
107
- doc1 := get (shownDoc .URI )
108
- checkMatch (doc1 , "const A =.*1" )
37
+ uri1 := viewPkgDoc (t , env , "a/a.go" )
38
+ doc1 := get (t , uri1 )
39
+ checkMatch (t , true , doc1 , "const A =.*1" )
109
40
110
41
// Check that edits to the buffer (even unsaved) are
111
42
// reflected in the HTML document.
112
43
env .RegexpReplace ("a/a.go" , "// EOF" , "func NewFunc() {}" )
113
44
env .Await (env .DoneDiagnosingChanges ())
114
- doc2 := get (shownDoc . URI )
115
- checkMatch (doc2 , "func NewFunc" )
45
+ doc2 := get (t , uri1 )
46
+ checkMatch (t , true , doc2 , "func NewFunc" )
116
47
117
48
// TODO(adonovan): assert some basic properties of the
118
49
// HTML document using something like
@@ -129,10 +60,10 @@ const A = 1
129
60
// downcall, this time for a "file:" URL, causing the
130
61
// client editor to navigate to the source file.
131
62
t .Log ("extracted /open URL" , openURL )
132
- get (openURL )
63
+ get (t , openURL )
133
64
134
65
// Check that that shown location is that of NewFunc.
135
- shownSource := shownDocument ("file:" )
66
+ shownSource := shownDocument (t , env , "file:" )
136
67
gotLoc := protocol.Location {
137
68
URI : protocol .DocumentURI (shownSource .URI ), // fishy conversion
138
69
Range : * shownSource .Selection ,
@@ -144,3 +75,127 @@ const A = 1
144
75
}
145
76
})
146
77
}
78
+
79
+ func TestRenderNoPanic66449 (t * testing.T ) {
80
+ // This particular input triggered a latent bug in doc.New
81
+ // that would corrupt the AST while filtering out unexported
82
+ // symbols such as b, causing nodeHTML to panic.
83
+ // Now it doesn't crash.
84
+ const files = `
85
+ -- go.mod --
86
+ module example.com
87
+
88
+ -- a/a.go --
89
+ package a
90
+
91
+ var A, b = 0, 0
92
+
93
+ func ExportedFunc()
94
+ func unexportedFunc()
95
+ type ExportedType int
96
+ type unexportedType int
97
+ `
98
+ Run (t , files , func (t * testing.T , env * Env ) {
99
+ uri1 := viewPkgDoc (t , env , "a/a.go" )
100
+ doc1 := get (t , uri1 )
101
+ // (Ideally our code rendering would also
102
+ // eliminate unexported symbols...)
103
+ // TODO(adonovan): when we emit var anchors,
104
+ // check that #A exists and #b does not.
105
+ checkMatch (t , true , doc1 , "var A, b = .*0.*0" )
106
+
107
+ // Unexported funcs/types/... must still be discarded.
108
+ checkMatch (t , true , doc1 , "ExportedFunc" )
109
+ checkMatch (t , false , doc1 , "unexportedFunc" )
110
+ checkMatch (t , true , doc1 , "ExportedType" )
111
+ checkMatch (t , false , doc1 , "unexportedType" )
112
+ })
113
+ }
114
+
115
+ // viewPkgDoc invokes the "View package documention" code action in
116
+ // the specified file. It returns the URI of the document, or fails
117
+ // the test.
118
+ func viewPkgDoc (t * testing.T , env * Env , filename string ) protocol.URI {
119
+ env .OpenFile (filename )
120
+
121
+ // Invoke the "View package documentation" code
122
+ // action to start the server.
123
+ var docAction * protocol.CodeAction
124
+ actions := env .CodeAction (filename , nil )
125
+ for _ , act := range actions {
126
+ if act .Title == "View package documentation" {
127
+ docAction = & act
128
+ break
129
+ }
130
+ }
131
+ if docAction == nil {
132
+ t .Fatalf ("can't find action with Title 'View package documentation', only %#v" ,
133
+ actions )
134
+ }
135
+
136
+ // Execute the command.
137
+ // Its side effect should be a single showDocument request.
138
+ params := & protocol.ExecuteCommandParams {
139
+ Command : docAction .Command .Command ,
140
+ Arguments : docAction .Command .Arguments ,
141
+ }
142
+ var result command.DebuggingResult
143
+ env .ExecuteCommand (params , & result )
144
+
145
+ doc := shownDocument (t , env , "http:" )
146
+ if doc == nil {
147
+ t .Fatalf ("no showDocument call had 'http:' prefix" )
148
+ }
149
+ t .Log ("showDocument(package doc) URL:" , doc .URI )
150
+ return doc .URI
151
+ }
152
+
153
+ // shownDocument returns the first shown document matching the URI prefix.
154
+ // It may be nil.
155
+ //
156
+ // TODO(adonovan): the integration test framework
157
+ // needs a way to reset ShownDocuments so they don't
158
+ // accumulate, necessitating the fragile prefix hack.
159
+ func shownDocument (t * testing.T , env * Env , prefix string ) * protocol.ShowDocumentParams {
160
+ t .Helper ()
161
+ var shown []* protocol.ShowDocumentParams
162
+ env .Await (ShownDocuments (& shown ))
163
+ var first * protocol.ShowDocumentParams
164
+ for _ , sd := range shown {
165
+ if strings .HasPrefix (sd .URI , prefix ) {
166
+ if first != nil {
167
+ t .Errorf ("got multiple showDocument requests: %#v" , shown )
168
+ break
169
+ }
170
+ first = sd
171
+ }
172
+ }
173
+ return first
174
+ }
175
+
176
+ // get fetches the content of a document over HTTP.
177
+ func get (t * testing.T , url string ) []byte {
178
+ t .Helper ()
179
+ resp , err := http .Get (url )
180
+ if err != nil {
181
+ t .Fatal (err )
182
+ }
183
+ defer resp .Body .Close ()
184
+ got , err := io .ReadAll (resp .Body )
185
+ if err != nil {
186
+ t .Fatal (err )
187
+ }
188
+ return got
189
+ }
190
+
191
+ // checkMatch asserts that got matches (or doesn't match, if !want) the pattern.
192
+ func checkMatch (t * testing.T , want bool , got []byte , pattern string ) {
193
+ t .Helper ()
194
+ if regexp .MustCompile (pattern ).Match (got ) != want {
195
+ if want {
196
+ t .Errorf ("input did not match wanted pattern %q; got:\n %s" , pattern , got )
197
+ } else {
198
+ t .Errorf ("input matched unwanted pattern %q; got:\n %s" , pattern , got )
199
+ }
200
+ }
201
+ }
0 commit comments