forked from fatih/vim-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest.vim
328 lines (278 loc) · 11.7 KB
/
test.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" Test runs `go test` in the current directory. If compile is true, it'll
" compile the tests instead of running them (useful to catch errors in the
" test files). Any other argument is appended to the final `go test` command.
function! go#test#Test(bang, compile, ...) abort
let args = ["test"]
if len(go#config#BuildTags()) > 0
call extend(args, ["-tags", go#config#BuildTags()])
endif
" don't run the test, only compile it. Useful to capture and fix errors.
if a:compile
let testfile = tempname() . ".vim-go.test"
call extend(args, ["-c", "-o", testfile])
endif
if a:0
let goargs = a:000
" do not expand for coverage mode as we're passing the arg ourself
if a:1 != '-coverprofile'
" expand all wildcards(i.e: '%' to the current file name)
let goargs = map(copy(a:000), "expand(v:val)")
endif
call extend(args, goargs, 1)
else
" only add this if no custom flags are passed
let timeout = go#config#TestTimeout()
call add(args, printf("-timeout=%s", timeout))
endif
if go#config#TermEnabled()
call go#term#new(a:bang, ["go"] + args, s:errorformat())
return
endif
if go#util#has_job()
" use vim's job functionality to call it asynchronously
let job_options = {
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ 'statustype': 'test',
\ 'errorformat': s:errorformat(),
\ }
if a:compile
let job_options.statustype = 'compile ' . job_options.statustype
endif
call s:test_job(['go'] + args, job_options)
return
endif
if go#config#EchoCommandInfo()
if a:compile
call go#util#EchoProgress("compiling tests ...")
else
call go#util#EchoProgress("testing...")
endif
endif
call go#cmd#autowrite()
redraw
let l:cmd = ['go'] + l:args
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
" TODO(bc): When the output is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
let l:listtype = go#list#Type("GoTest")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
execute cd fnameescape(expand("%:p:h"))
if l:err != 0
let l:winid = win_getid(winnr())
call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), l:cmd, 0)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(out)
elseif a:bang
call win_gotoid(l:winid)
else
call go#list#JumpToFirst(l:listtype)
endif
else
call go#list#Clean(l:listtype)
if a:compile
call go#util#EchoSuccess("[test] SUCCESS")
else
call go#util#EchoSuccess("[test] PASS")
endif
endif
execute cd . fnameescape(dir)
endfunction
" Testfunc runs a single test that surrounds the current cursor position.
" Arguments are passed to the `go test` command.
function! go#test#Func(bang, ...) abort
let l:test = go#util#TestName()
if l:test is ''
call go#util#EchoWarning("[test] no test found immediate to cursor")
return
endif
let args = [a:bang, 0, "-run", l:test . "$"]
if a:0
call extend(args, a:000)
else
" only add this if no custom flags are passed
let timeout = go#config#TestTimeout()
call add(args, printf("-timeout=%s", timeout))
endif
call call('go#test#Test', args)
endfunction
function! s:test_job(cmd, args) abort
" autowrite is not enabled for jobs
call go#cmd#autowrite()
call go#job#Spawn(a:cmd, a:args)
endfunction
let s:efm = ""
let s:go_test_show_name = 0
function! s:errorformat() abort
" NOTE(arslan): once we get JSON output everything will be easier :).
" TODO(bc): When the output is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
" https://github.com/golang/go/issues/2981.
let goroot = go#util#goroot()
let show_name = go#config#TestShowName()
if s:efm != "" && s:go_test_show_name == show_name
return s:efm
endif
let s:go_test_show_name = show_name
" each level of test indents the test output 4 spaces. Capturing groups
" (e.g. \(\)) cannot be used in an errorformat, but non-capturing groups can
" (e.g. \%(\)).
let indent = '%\\%( %\\)'
" ignore `go test -v` output for starting tests
let format = "%-G=== RUN %.%#"
" ignore `go test -v` output for passing tests
let format .= ",%-G" . indent . "%#--- PASS: %.%#"
" Match failure lines.
" Example failures start with '--- FAIL: ', followed by the example name
" followed by a space , followed by the duration of the example in
" parantheses. They aren't nested, though, so don't check for indentation.
" The errors from them also aren't indented and don't report file location
" or line numbers, so those won't show up. This will at least let the user
" know which example failed, though.
let format .= ',%G--- FAIL: %\\%(Example%\\)%\\@=%m (%.%#)'
" Test failures start with '--- FAIL: ', followed by the test name followed
" by a space, followed by the duration of the test in parentheses.
"
" e.g.:
" '--- FAIL: TestSomething (0.00s)'
if show_name
let format .= ",%G" . indent . "%#--- FAIL: %m (%.%#)"
else
let format .= ",%-G" . indent . "%#--- FAIL: %.%#"
endif
" Go 1.10 test output {{{1
" Matches test output lines.
"
" All test output lines start with the test indentation and a tab, followed
" by the filename, a colon, the line number, another colon, a space, and the
" message. e.g.:
" '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
let format .= ",%A" . indent . "%#%\\t%\\+%f:%l: %m"
" also match lines that don't have a message (i.e. the message begins with a
" newline or is the empty string):
" e.g.:
" t.Errorf("\ngot %v; want %v", actual, expected)
" t.Error("")
let format .= ",%A" . indent . "%#%\\t%\\+%f:%l: "
" Match the 2nd and later lines of multi-line output. These lines are
" indented the number of spaces for the level of nesting of the test,
" followed by two tabs, followed by the message.
"
" Treat these lines as if they are stand-alone lines of output by using %G.
" It would also be valid to treat these lines as if they were the
" continuation of a multi-line error by using %C instead of %G, but that
" would also require that all test errors using a %A or %E modifier to
" indicate that they're multiple lines of output, but in that case the lines
" get concatenated in the quickfix list, which is not what users typically
" want when writing a newline into their test output.
let format .= ",%G" . indent . "%#%\\t%\\{2}%m"
" }}}1
" Go 1.14 test verbose output {{{1
" Match test output lines similarly to Go 1.11 test output lines, but they
" have the test name followed by a colon before the filename when run with
" the -v flag.
let format .= ",%A" . indent . "%\\+%[%^:]%\\+: %f:%l: %m"
let format .= ",%A" . indent . "%\\+%[%^:]%\\+: %f:%l: "
" }}}1
" Go 1.11 test output {{{1
" Match test output lines similarly to Go 1.10 test output lines, but they
" use an indent level where the Go 1.10 test output uses tabs, so they'll
" always have at least one level indentation...
let format .= ",%A" . indent . "%\\+%f:%l: %m"
let format .= ",%A" . indent . "%\\+%f:%l: "
let format .= ",%G" . indent . "%\\{2\\,}%m"
" }}}1
" set the format for panics.
" handle panics from test timeouts
let format .= ",%+Gpanic: test timed out after %.%\\+"
" handle non-timeout panics
" In addition to 'panic', check for 'fatal error' to support older versions
" of Go that used 'fatal error'.
"
" Panics come in two flavors. When the goroutine running the tests panics,
" `go test` recovers and tries to exit more cleanly. In that case, the panic
" message is suffixed with ' [recovered]'. If the panic occurs in a
" different goroutine, it will not be suffixed with ' [recovered]'.
let format .= ",%+Afatal error: %.%# [recovered]"
let format .= ",%+Apanic: %.%# [recovered]"
let format .= ",%+Afatal error: %.%#"
let format .= ",%+Apanic: %.%#"
" Match address lines in stacktraces produced by panic.
"
" Address lines in the stack trace have leading tabs, followed by the path
" to the file. The file path is followed by a colon and then the line number
" within the file where the panic occurred. After that there's a space and
" hexadecimal number.
"
" e.g.:
" '\t/usr/local/go/src/time.go:1313 +0x5d'
" panicaddress and readyaddress are identical except for
" panicaddress sets the filename and line number.
let panicaddress = "%\\t%f:%l +0x%[0-9A-Fa-f]%\\+"
let readyaddress = "%\\t%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
" stdlib address is identical to readyaddress, except it matches files
" inside GOROOT.
let stdlibaddress = "%\\t" . goroot . "%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
" Match and ignore the running goroutine line.
let format .= ",%-Cgoroutine %\\d%\\+ [running]:"
" Match address lines that refer to stdlib, but consider them informational
" only. This is to catch the lines after the first address line in the
" running goroutine of a panic stack trace. Ideally, this wouldn't be
" necessary, but when a panic happens in the goroutine running a test, it's
" recovered and another panic is created, so the stack trace actually has
" the line that caused the original panic a couple of addresses down the
" stack.
let format .= ",%-C" . stdlibaddress
" Match address lines in the first matching goroutine. This means the panic
" message will only be shown as the error message in the first address of
" the running goroutine's stack.
let format .= ",%Z" . panicaddress
" Match and ignore errors from runtime.goparkunlock(). These started
" appearing in stack traces from Go 1.12 test timeouts.
let format .= ",%-Gruntime.goparkunlock(%.%#"
let format .= ",%-G%\\t" . goroot . "%\\f%\\+:%\\d%\\+"
" Match and ignore panic address without being part of a multi-line message.
" This is to catch those lines that come after the top most non-standard
" library line in stack traces.
let format .= ",%-G" . readyaddress
" Match and ignore exit status lines (produced when go test panics) whether
" part of a multi-line message or not, because these lines sometimes come
" before and sometimes after panic stacktraces.
let format .= ",%-Cexit status %[0-9]%\\+"
"let format .= ",exit status %[0-9]%\\+"
" Match and ignore failure lines whether part of a multi-line message
" or not, because these lines sometimes come before and sometimes after
" panic stacktraces.
let format .= ",%-CFAIL%\\t%.%#"
"let format .= ",FAIL%\\t%.%#"
" match compiler errors.
" These are very smilar to errors from <=go1.10 test output, but lack
" leading tabs for the first line of an error, and subsequent lines only
" have one tab instead of two.
let format .= ",%A%f:%l:%c: %m"
let format .= ",%A%f:%l: %m"
" It would be nice if this weren't necessary, but panic lines from tests are
" prefixed with a single leading tab, making them very similar to 2nd and
" later lines of a multi-line compiler error. Swallow it so that it doesn't
" cause a quickfix entry since the next %G entry can add a quickfix entry
" for 2nd and later lines of a multi-line compiler error.
let format .= ",%-C%\\tpanic: %.%#"
let format .= ",%G%\\t%m"
" Match and ignore everything else in multi-line messages.
let format .= ",%-C%.%#"
" Match and ignore everything else not in a multi-line message:
let format .= ",%-G%.%#"
let s:efm = format
return s:efm
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et