blob: 7113602b67c512df17b8c5e60875ac8cbc25a367 [file] [log] [blame]
Bram Moolenaar46acad72023-06-11 19:04:18 +01001" Runs all the syntax tests for which there is no "done/name" file.
2"
3" Current directory must be runtime/syntax.
4
Christian Brabandtde79f912024-10-24 23:03:10 +02005" needed because of line-continuation lines
6set cpo&vim
7
Bram Moolenaar46acad72023-06-11 19:04:18 +01008" Only do this with the +eval feature
9if 1
10
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +010011" Remember the directory where we started. Will change to "testdir" below.
12let syntaxDir = getcwd()
13
14let s:messagesFname = fnameescape(syntaxDir .. '/testdir/messages')
15
16let s:messages = []
17
18" Add one message to the list of messages
19func Message(msg)
20 echomsg a:msg
21 call add(s:messages, a:msg)
22endfunc
23
24" Report a fatal message and exit
25func Fatal(msg)
26 echoerr a:msg
27 call AppendMessages(a:msg)
28 qall!
29endfunc
30
31" Append s:messages to the messages file and make it empty.
32func AppendMessages(header)
33 exe 'split ' .. s:messagesFname
34 call append(line('$'), '')
35 call append(line('$'), a:header)
36 call append(line('$'), s:messages)
37 let s:messages = []
38 wq
39endfunc
40
41" Relevant messages are written to the "messages" file.
42" If the file already exists it is appended to.
43exe 'split ' .. s:messagesFname
44call append(line('$'), repeat('=-', 70))
45call append(line('$'), '')
Bram Moolenaar031d6322023-06-22 22:38:54 +010046let s:test_run_message = 'Test run on ' .. strftime("%Y %b %d %H:%M:%S")
47call append(line('$'), s:test_run_message)
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +010048wq
49
50if syntaxDir !~ '[/\\]runtime[/\\]syntax\>'
51 call Fatal('Current directory must be "runtime/syntax"')
Bram Moolenaar46acad72023-06-11 19:04:18 +010052endif
53if !isdirectory('testdir')
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +010054 call Fatal('"testdir" directory not found')
Bram Moolenaar46acad72023-06-11 19:04:18 +010055endif
56
57" Use the script for source code screendump testing. It sources other scripts,
58" therefore we must "cd" there.
59cd ../../src/testdir
60source screendump.vim
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +010061exe 'cd ' .. fnameescape(syntaxDir)
Bram Moolenaar46acad72023-06-11 19:04:18 +010062
63" For these tests we need to be able to run terminal Vim with 256 colors. On
64" MS-Windows the console only has 16 colors and the GUI can't run in a
65" terminal.
66if !CanRunVimInTerminal()
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +010067 call Fatal('Cannot make screendumps, aborting')
Bram Moolenaar46acad72023-06-11 19:04:18 +010068endif
69
70cd testdir
71if !isdirectory('done')
72 call mkdir('done')
73endif
74
75set nocp
76set nowrapscan
77set report=9999
78set modeline
79set debug=throw
80set nomore
81
82au! SwapExists * call HandleSwapExists()
83func HandleSwapExists()
84 " Ignore finding a swap file for the test input, the user might be editing
85 " it and that's OK.
86 if expand('<afile>') =~ 'input[/\\].*\..*'
87 let v:swapchoice = 'e'
88 endif
89endfunc
90
Aliaksei Budavei84184462024-05-21 01:10:26 +030091def IsWinNumOneAtEOF(in_name_and_out_name: string): bool
92 # Expect defaults from term_util#RunVimInTerminal().
93 if winwidth(1) != 75 || winheight(1) != 20
94 ch_log(printf('Aborting for %s: (75 x 20) != (%d x %d)',
95 in_name_and_out_name,
96 winwidth(1),
97 winheight(1)))
98 return true
99 endif
100 # A two-fold role: (1) redraw whenever the first test file is of 19 lines or
101 # less long (not applicable to c.c); (2) redraw in case the terminal buffer
102 # cannot redraw itself just yet (else expect extra files generated).
103 redraw
104 const pos: string = join([
105 screenstring(20, 71),
106 screenstring(20, 72),
107 screenstring(20, 73),
108 screenstring(20, 74),
109 screenstring(20, 75)], '')
110 return (pos == ' All ' || pos == ' Bot ')
111enddef
112
Christian Brabandt56824432024-02-28 21:24:25 +0100113func RunTest()
114 let ok_count = 0
115 let failed_tests = []
116 let skipped_count = 0
117 let MAX_FAILED_COUNT = 5
Aliaksei Budaveif6069a72024-03-05 22:34:36 +0300118 " Create a map of setup configuration filenames with their basenames as keys.
119 let setup = glob('input/setup/*.vim', 1, 1)
120 \ ->reduce({d, f -> extend(d, {fnamemodify(f, ':t:r'): f})}, {})
Aliaksei Budaveiec022942024-10-06 16:57:33 +0200121 " Turn a subset of filenames etc. requested for testing into a pattern.
122 let filter = filereadable('../testdir/Xfilter')
123 \ ? readfile('../testdir/Xfilter')
124 \ ->map({_, v -> (v =~ '\.' ? '^' : '\.') .. v .. '$'})
125 \ ->join('\|')
126 \ : ''
Aliaksei Budaveif6069a72024-03-05 22:34:36 +0300127
Aliaksei Budaveiec022942024-10-06 16:57:33 +0200128 " Treat "\.self-testing$" as a string NOT as a regexp.
129 if filter ==# '\.self-testing$'
Aliaksei Budaveid2f49872024-05-24 19:14:16 +0300130 let dirpath = 'input/selftestdir/'
Aliaksei Budaveid33afe12024-08-12 18:37:15 +0200131 let fnames = readdir(dirpath, {fname -> fname !~ '^README\.txt$'})
Aliaksei Budaveid2f49872024-05-24 19:14:16 +0300132 else
133 let dirpath = 'input/'
Aliaksei Budaveiec022942024-10-06 16:57:33 +0200134 let filter ..= exists("$VIM_SYNTAX_TEST_FILTER") &&
135 \ !empty($VIM_SYNTAX_TEST_FILTER)
136 \ ? (empty(filter) ? '' : '\|') .. $VIM_SYNTAX_TEST_FILTER
137 \ : ''
138 let fnames = readdir(dirpath,
139 \ {subset -> {fname -> fname !~ '\~$' && fname =~# subset}}(
140 \ empty(filter) ? '^.\+\..\+$' : filter))
Aliaksei Budaveid2f49872024-05-24 19:14:16 +0300141 endif
Bram Moolenaar7d0dbd02023-06-24 00:56:50 +0100142
Aliaksei Budaveid2f49872024-05-24 19:14:16 +0300143 for fname in fnames
144 let root = fnamemodify(fname, ':r')
145 let fname = dirpath .. fname
Christian Brabandt56824432024-02-28 21:24:25 +0100146 let filetype = substitute(root, '\([^_.]*\)[_.].*', '\1', '')
147 let failed_root = 'failed/' .. root
Bram Moolenaar46acad72023-06-11 19:04:18 +0100148
Christian Brabandt56824432024-02-28 21:24:25 +0100149 " Execute the test if the "done" file does not exist or when the input file
150 " is newer.
151 let in_time = getftime(fname)
152 let out_time = getftime('done/' .. root)
153 if out_time < 0 || in_time > out_time
154 call ch_log('running tests for: ' .. fname)
Bram Moolenaar7d0dbd02023-06-24 00:56:50 +0100155
Christian Brabandt56824432024-02-28 21:24:25 +0100156 for dumpname in glob(failed_root .. '_\d*\.dump', 1, 1)
157 call delete(dumpname)
158 endfor
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +0100159 call delete('done/' .. root)
160
Christian Brabandt56824432024-02-28 21:24:25 +0100161 let lines =<< trim END
Aliaksei Budavei71971432024-07-05 21:30:02 +0300162 " Track the cursor progress through a syntax test file so that any
163 " degenerate input can be reported. Each file will have its own cursor.
164 let s:cursor = 1
165
Christian Brabandt56824432024-02-28 21:24:25 +0100166 " extra info for shell variables
167 func ShellInfo()
168 let msg = ''
169 for [key, val] in items(b:)
170 if key =~ '^is_'
171 let msg ..= key .. ': ' .. val .. ', '
172 endif
173 endfor
174 if msg != ''
175 echomsg msg
176 endif
177 endfunc
178
179 au! SwapExists * call HandleSwapExists()
180 func HandleSwapExists()
181 " Ignore finding a swap file for the test input, the user might be
182 " editing it and that's OK.
183 if expand('<afile>') =~ 'input[/\\].*\..*'
184 let v:swapchoice = 'e'
185 endif
186 endfunc
187
188 func LoadFiletype(type)
189 for file in glob("ftplugin/" .. a:type .. "*.vim", 1, 1)
190 exe "source " .. file
191 endfor
192 redraw!
193 endfunc
194
Aliaksei Budavei93edd252024-03-05 22:34:36 +0300195 func SetUpVim()
196 call cursor(1, 1)
Aliaksei Budaveia2addeb2024-03-18 20:39:32 +0100197 " Defend against rogue VIM_TEST_SETUP commands.
Aliaksei Budavei93edd252024-03-05 22:34:36 +0300198 for _ in range(20)
Aliaksei Budaveia2addeb2024-03-18 20:39:32 +0100199 let lnum = search('\C\<VIM_TEST_SETUP\>', 'eW', 20)
Aliaksei Budavei93edd252024-03-05 22:34:36 +0300200 if lnum < 1
201 break
202 endif
Aliaksei Budaveia2addeb2024-03-18 20:39:32 +0100203 exe substitute(getline(lnum), '\C.*\<VIM_TEST_SETUP\>', '', '')
Aliaksei Budavei93edd252024-03-05 22:34:36 +0300204 endfor
205 call cursor(1, 1)
206 " BEGIN [runtime/defaults.vim]
Aliaksei Budaveia2addeb2024-03-18 20:39:32 +0100207 " Also, disable italic highlighting to avoid issues on some terminals.
Aliaksei Budavei84184462024-05-21 01:10:26 +0300208 set display=lastline ruler scrolloff=5 t_ZH= t_ZR=
Aliaksei Budavei93edd252024-03-05 22:34:36 +0300209 syntax on
210 " END [runtime/defaults.vim]
211 redraw!
212 endfunc
Aliaksei Budavei84184462024-05-21 01:10:26 +0300213
Aliaksei Budavei71971432024-07-05 21:30:02 +0300214 def s:AssertCursorForwardProgress(): bool
215 const curnum: number = line('.')
216 if curnum <= cursor
217 # Use "actions/upload-artifact@v4" of ci.yml for delivery.
218 writefile([printf('No cursor progress: %d <= %d (%s). Please file an issue.',
219 curnum,
220 cursor,
221 bufname('%'))],
222 'failed/00-FIXME',
223 'a')
224 bwipeout!
225 endif
226 cursor = curnum
227 return true
228 enddef
229
230 def ScrollToSecondPage(estate: number, op_wh: number, op_so: number): bool
Aliaksei Budavei84184462024-05-21 01:10:26 +0300231 if line('.') != 1 || line('w$') >= line('$')
Aliaksei Budavei71971432024-07-05 21:30:02 +0300232 return AssertCursorForwardProgress()
Aliaksei Budavei84184462024-05-21 01:10:26 +0300233 endif
234 try
235 set scrolloff=0
236 # Advance mark "c"[ursor] along with the cursor.
237 norm! Lmc
238 if foldclosed('.') < 0 &&
239 (strdisplaywidth(getline('.')) + &l:fdc * winheight(1)) >= estate
240 # Make for an exit for a screenful long line.
241 norm! j^
Aliaksei Budavei71971432024-07-05 21:30:02 +0300242 return AssertCursorForwardProgress()
Aliaksei Budavei84184462024-05-21 01:10:26 +0300243 else
244 # Place the cursor on the actually last visible line.
245 while winline() < op_wh
246 const lastnum: number = winline()
247 norm! gjmc
248 if lastnum > winline()
249 break
250 endif
251 endwhile
252 norm! zt
253 endif
254 finally
255 # COMPATIBILITY: Scroll up around "scrolloff" lines.
256 &scrolloff = max([1, op_so])
257 endtry
258 norm! ^
Aliaksei Budavei71971432024-07-05 21:30:02 +0300259 return AssertCursorForwardProgress()
Aliaksei Budavei84184462024-05-21 01:10:26 +0300260 enddef
261
Aliaksei Budavei71971432024-07-05 21:30:02 +0300262 def ScrollToNextPage(estate: number, op_wh: number, op_so: number): bool
Aliaksei Budavei84184462024-05-21 01:10:26 +0300263 if line('.') == 1 || line('w$') >= line('$')
Aliaksei Budavei71971432024-07-05 21:30:02 +0300264 return AssertCursorForwardProgress()
Aliaksei Budavei84184462024-05-21 01:10:26 +0300265 endif
266 try
267 set scrolloff=0
268 # Advance mark "c"[ursor] along with the cursor.
269 norm! Lmc
270 if foldclosed('.') < 0 &&
271 (strdisplaywidth(getline('.')) + &l:fdc * winheight(1)) >= estate
272 # Make for an exit for a screenful long line.
273 norm! j^
Aliaksei Budavei71971432024-07-05 21:30:02 +0300274 return AssertCursorForwardProgress()
Aliaksei Budavei84184462024-05-21 01:10:26 +0300275 else
276 # Place the cursor on the actually last visible line.
277 while winline() < op_wh
278 const lastnum: number = winline()
279 norm! gjmc
280 if lastnum > winline()
281 break
282 endif
283 endwhile
284 endif
285 finally
286 # COMPATIBILITY: Scroll up/down around "scrolloff" lines.
287 &scrolloff = max([1, op_so])
288 endtry
289 norm! zt
290 const marknum: number = line("'c")
291 # Eschew &smoothscroll since line("`c") is not supported.
292 # Remember that "w0" can point to the first line of a _closed_ fold
293 # whereas the last line of a _closed_ fold can be marked.
294 if line('w0') > marknum
295 while line('w0') > marknum
296 exe "norm! \<C-y>"
297 endwhile
298 if line('w0') != marknum
299 exe "norm! \<C-e>H"
300 endif
301 # Handle non-wrapped lines.
302 elseif line('w0') < marknum
303 while line('w0') < marknum
304 exe "norm! \<C-e>"
305 endwhile
306 if line('w0') != marknum
307 exe "norm! \<C-y>H"
308 endif
309 endif
310 norm! ^
Aliaksei Budavei71971432024-07-05 21:30:02 +0300311 return AssertCursorForwardProgress()
Aliaksei Budavei84184462024-05-21 01:10:26 +0300312 enddef
Christian Brabandt56824432024-02-28 21:24:25 +0100313 END
314 call writefile(lines, 'Xtestscript')
315
316 " close all but the last window
317 while winnr('$') > 1
318 close
319 endwhile
320
321 " Redraw to make sure that messages are cleared and there is enough space
322 " for the terminal window.
323 redraw
324
Aliaksei Budavei93edd252024-03-05 22:34:36 +0300325 " Let "Xtestscript#SetUpVim()" turn the syntax on.
Aliaksei Budaveif6069a72024-03-05 22:34:36 +0300326 let prefix = '-Nu NONE -S Xtestscript'
327 let path = get(setup, root, '')
328 " Source the found setup configuration file.
329 let args = !empty(path)
330 \ ? prefix .. ' -S ' .. path
331 \ : prefix
332 let buf = RunVimInTerminal(args, {})
Christian Brabandt56824432024-02-28 21:24:25 +0100333 " edit the file only after catching the SwapExists event
334 call term_sendkeys(buf, ":edit " .. fname .. "\<CR>")
Aliaksei Budavei93edd252024-03-05 22:34:36 +0300335 " set up the testing environment
336 call term_sendkeys(buf, ":call SetUpVim()\<CR>")
Christian Brabandt56824432024-02-28 21:24:25 +0100337 " load filetype specific settings
338 call term_sendkeys(buf, ":call LoadFiletype('" .. filetype .. "')\<CR>")
339
340 if filetype == 'sh'
341 call term_sendkeys(buf, ":call ShellInfo()\<CR>")
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +0100342 endif
Christian Brabandt56824432024-02-28 21:24:25 +0100343
344 " Screendump at the start of the file: failed/root_00.dump
345 let root_00 = root .. '_00'
Aliaksei Budavei84184462024-05-21 01:10:26 +0300346 let in_name_and_out_name = fname .. ': failed/' .. root_00 .. '.dump'
347 call ch_log('First screendump for ' .. in_name_and_out_name)
Christian Brabandt56824432024-02-28 21:24:25 +0100348 let fail = VerifyScreenDump(buf, root_00, {})
349
Christian Brabandt56824432024-02-28 21:24:25 +0100350 " Make a Screendump every 18 lines of the file: failed/root_NN.dump
Christian Brabandt56824432024-02-28 21:24:25 +0100351 let nr = 1
Aliaksei Budavei84184462024-05-21 01:10:26 +0300352 let root_next = printf('%s_%02d', root, nr)
353 let in_name_and_out_name = fname .. ': failed/' .. root_next .. '.dump'
354
Aliaksei Budavei71971432024-07-05 21:30:02 +0300355 " Accommodate the next code block to "buf"'s contingency for self
356 " wipe-out.
357 try
358 if !IsWinNumOneAtEOF(in_name_and_out_name)
359 call term_sendkeys(buf, ":call ScrollToSecondPage((18 * 75 + 1), 19, 5) | redraw!\<CR>")
Aliaksei Budavei84184462024-05-21 01:10:26 +0300360 call ch_log('Next screendump for ' .. in_name_and_out_name)
361 let fail += VerifyScreenDump(buf, root_next, {})
362 let nr += 1
363 let root_next = printf('%s_%02d', root, nr)
364 let in_name_and_out_name = fname .. ': failed/' .. root_next .. '.dump'
Christian Brabandt56824432024-02-28 21:24:25 +0100365
Aliaksei Budavei71971432024-07-05 21:30:02 +0300366 while !IsWinNumOneAtEOF(in_name_and_out_name)
367 call term_sendkeys(buf, ":call ScrollToNextPage((18 * 75 + 1), 19, 5) | redraw!\<CR>")
368 call ch_log('Next screendump for ' .. in_name_and_out_name)
369 let fail += VerifyScreenDump(buf, root_next, {})
370 let nr += 1
371 let root_next = printf('%s_%02d', root, nr)
372 let in_name_and_out_name = fname .. ': failed/' .. root_next .. '.dump'
373 endwhile
374 endif
Aliaksei Budavei71971432024-07-05 21:30:02 +0300375 call StopVimInTerminal(buf)
376 finally
377 call delete('Xtestscript')
378 endtry
Christian Brabandt56824432024-02-28 21:24:25 +0100379
380 " redraw here to avoid the following messages to get mixed up with screen
381 " output.
382 redraw
383
384 " Add any assert errors to s:messages.
385 if len(v:errors) > 0
386 call extend(s:messages, v:errors)
387 " Echo the errors here, in case the script aborts or the "messages" file
388 " is not displayed later.
389 echomsg v:errors
390 let v:errors = []
391 let fail += 1
392 endif
393
394 if fail == 0
395 call Message("Test " .. root .. " OK")
396
397 call writefile(['OK'], 'done/' .. root)
398
399 let ok_count += 1
400 else
401 call Message("Test " .. root .. " FAILED")
402
403 call delete('done/' .. root)
404
405 eval failed_tests->add(root)
406 if len(failed_tests) > MAX_FAILED_COUNT
407 call Message('')
408 call Message('Too many errors, aborting')
409 endif
410 endif
411 else
412 call Message("Test " .. root .. " skipped")
413 let skipped_count += 1
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +0100414 endif
Christian Brabandt56824432024-02-28 21:24:25 +0100415
416 " Append messages to the file "testdir/messages"
417 call AppendMessages('Input file ' .. fname .. ':')
418
419 if len(failed_tests) > MAX_FAILED_COUNT
420 break
421 endif
422 endfor
423
424 call Message(s:test_run_message)
425 call Message('OK: ' .. ok_count)
426 call Message('FAILED: ' .. len(failed_tests) .. ': ' .. string(failed_tests))
427 call Message('skipped: ' .. skipped_count)
Aliaksei Budaveid33afe12024-08-12 18:37:15 +0200428
429 if !empty(failed_tests)
430 call Message('')
431 call Message('View generated screendumps with "../../src/vim --clean -S testdir/viewdumps.vim"')
432 endif
433
Christian Brabandt203c7222024-10-29 20:21:42 +0100434 call AppendMessages('== SUMMARY SYNTAX TESTS ==')
Christian Brabandt56824432024-02-28 21:24:25 +0100435
436 if len(failed_tests) > 0
437 " have make report an error
438 cquit
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +0100439 endif
Christian Brabandt56824432024-02-28 21:24:25 +0100440endfunc
Bram Moolenaar1aa5f1c2023-06-22 21:57:51 +0100441
Christian Brabandt56824432024-02-28 21:24:25 +0100442call RunTest()
Christian Brabandt627c9502024-02-10 13:02:17 +0100443
444" Matching "if 1" at the start.
445endif
446
Bram Moolenaar46acad72023-06-11 19:04:18 +0100447qall!
Christian Brabandt56824432024-02-28 21:24:25 +0100448
449" vim:ts=8