blob: 8aa9abcd2b71923f98a74c1c6eec76d6bfc085f7 [file] [log] [blame]
Christian Brabandteb380b92025-07-07 20:53:55 +02001source util/window_manager.vim
Foxe Chenb90c2392025-06-27 21:10:35 +02002
3CheckFeature wayland
4CheckFeature wayland_clipboard
5CheckUnix
6CheckFeature job
7CheckWaylandCompositor
8CheckNotGui
9
10if !executable('wl-paste') || !executable('wl-copy')
11 throw "Skipped: wl-clipboard is not available"
12endif
13
14" Process will be killed when the test ends
15let s:global_wayland_display = StartWaylandCompositor()
16let s:old_wayland_display = $WAYLAND_DISPLAY
17
18" For some reason if $WAYLAND_DISPLAY is set in the global namespace (not in a
19" function), it won't actually be set if $WAYLAND_DISPLAY was not set before
20" (such as in a CI environment) ? Solution is to just set it before the code of
21" every test function
22func s:PreTest()
23 let $WAYLAND_DISPLAY=s:global_wayland_display
24 exe 'wlrestore ' .. $WAYLAND_DISPLAY
25
26 set cpm=wayland
27endfunc
28
29func s:SetupFocusStealing()
30 if !executable('wayland-info')
31 throw "Skipped: wayland-info program not available"
32 endif
33
34 " Starting a headless compositor won't expose a keyboard capability for its
35 " seat, so we must use the user's existing Wayland session if they are in one.
36 let $WAYLAND_DISPLAY = s:old_wayland_display
37
38 exe 'wlrestore ' .. $WAYLAND_DISPLAY
39
40 " Check if we have keyboard capability for seat
41 if system("wayland-info -i wl_seat | grep capabilities") !~? "keyboard"
42 throw "Skipped: seat does not have keyboard"
43 endif
44
45 let $VIM_WAYLAND_FORCE_FS=1
46 wlrestore!
47endfunc
48
49func s:UnsetupFocusStealing()
50 unlet $VIM_WAYLAND_FORCE_FS
51endfunc
52
53" Need X connection for tests that use client server communication
54func s:CheckXConnection()
55 if has('x11')
56 try
57 call remote_send('xxx', '')
58 catch
59 if v:exception =~ 'E240:'
60 throw 'Skipped: no connection to the X server'
61 endif
62 " ignore other errors
63 endtry
64 endif
65endfunc
66
67func s:EndRemoteVim(name, job)
68 eval remote_send(a:name, "\<Esc>:qa!\<CR>")
69 try
70 call WaitForAssert({-> assert_equal("dead", job_status(a:job))})
71 finally
72 if job_status(a:job) != 'dead'
73 call assert_report('Vim instance did not exit')
74 call job_stop(a:job, 'kill')
75 endif
76 endtry
77endfunc
78
79func Test_wayland_startup()
80 call s:PreTest()
81 call s:CheckXConnection()
82
83 let l:name = 'WLVIMTEST'
84 let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
85 let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
86
87 call WaitForAssert({-> assert_equal("run", job_status(l:job))})
88 call WaitForAssert({-> assert_match(name, serverlist())})
89
90 call WaitForAssert({-> assert_equal($WAYLAND_DISPLAY,
91 \ remote_expr(l:name, 'v:wayland_display'))})
92
93 call s:EndRemoteVim(l:name, l:job)
94
95 " When $WAYLAND_DISPLAY is invalid
96 let l:job = job_start(cmd, { 'stoponexit': 'kill', 'out_io': 'null',
97 \ 'env': {'WAYLAND_DISPLAY': 'UNKNOWN'}})
98
99 call WaitForAssert({-> assert_equal("run", job_status(l:job))})
100 call WaitForAssert({-> assert_match(name, serverlist())})
101
102 call assert_equal('', remote_expr(l:name, 'v:wayland_display'))
103 call s:EndRemoteVim(l:name, l:job)
104endfunc
105
106func Test_wayland_wlrestore()
107 call s:PreTest()
108
109 let l:wayland_display = StartWaylandCompositor()
110 let l:env_cmd = 'WAYLAND_DISPLAY=' .. l:wayland_display .. ' '
111
112 exe "wlrestore " .. l:wayland_display
113
114 call assert_equal(l:wayland_display, v:wayland_display)
115
Hirohito Higashi73b96502025-06-28 18:18:21 +0200116 " Check if calling wlrestore without arguments uses the existing Wayland
Foxe Chenb90c2392025-06-27 21:10:35 +0200117 " display.
118 wlrestore!
119 call assert_equal(l:wayland_display, v:wayland_display)
120
121 " If called with invalid display
122 wlrestore IDONTEXIST
123 call assert_equal("", v:wayland_display)
124
125 wlrestore!
126 call assert_equal("", v:wayland_display)
127
128 exe "wlrestore " .. l:wayland_display
129 call assert_equal(l:wayland_display, v:wayland_display)
130
131 " Actually check if connected display is different in case of regression with
132 " v:wayland_display
133 call system('wl-copy "1"')
134 call system(l:env_cmd .. 'wl-copy "2"')
135
136 call assert_equal('2', getreg('+'))
137
138 " Check if wlrestore doesn't disconnect the display if not nessecary by seeing
139 " if Vim doesn't lose the selection
140 call setreg('+', 'testing', 'c')
141
142 wlrestore
143 call assert_match('_VIM_TEXT', system(l:env_cmd .. 'wl-paste -l'))
144
145 " Forcibly disconnect and reconnect the display
146 wlrestore!
147 call assert_notmatch('_VIM_TEXT', system(l:env_cmd .. 'wl-paste -l'))
148
149 call EndWaylandCompositor(l:wayland_display)
150endfunc
151
Hirohito Higashi73b96502025-06-28 18:18:21 +0200152" Test behaviour when Wayland display connection is lost
Foxe Chenb90c2392025-06-27 21:10:35 +0200153func Test_wayland_connection_lost()
154 call s:PreTest()
155
156 let l:wayland_display = StartWaylandCompositor()
157 let l:env_cmd = 'WAYLAND_DISPLAY=' .. l:wayland_display .. ' '
158
159 exe "wlrestore " .. l:wayland_display
160
161 call system(l:env_cmd .. 'wl-copy test')
162
163 call assert_equal(l:wayland_display, v:wayland_display)
164 call assert_equal('test', getreg('+'))
165
166 call EndWaylandCompositor(l:wayland_display)
167
168 call assert_equal('', getreg('+'))
169 call assert_fails('put +', 'E353:')
170 call assert_fails('yank +', 'E1548:')
171endfunc
172
173" Basic paste tests
174func Test_wayland_paste()
175 call s:PreTest()
176
177 " Prevent 'Register changed while using it' error, guessing this works because
178 " it makes Vim lose the selection?
179 wlrestore!
180
181 " Regular selection
182 new
183
184 call system('wl-copy "TESTING"')
185 put +
186
187 call assert_equal("TESTING", getline(2))
188
189 call system('printf "LINE1\nLINE2\nLINE3" | wl-copy -n')
190 put +
191
192 call assert_equal(["LINE1", "LINE2", "LINE3"], getline(3, 5))
193 bw!
194
195 " Primary selection
196 new
197
198 call system('wl-copy -p "TESTING"')
199 put *
200
201 call assert_equal("TESTING", getline(2))
202
203 call system('printf "LINE1\nLINE2\nLINE3" | wl-copy -p')
204 put *
205
206 call assert_equal(["LINE1", "LINE2", "LINE3"], getline(3, 5))
207
208 bw!
209
210 " Check behaviour when selecton is cleared (empty)
211 call system('wl-copy --clear')
212 call assert_fails('put +', 'E353:')
213endfunc
214
215" Basic yank/copy tests
216func Test_wayland_yank()
217 call s:PreTest()
218
219 wlrestore!
220
221 new
222
223 call setline(1, 'testing')
224 yank +
225
226 call assert_equal("testing\n", system('wl-paste -n'))
227
228 call setline(2, 'testing2')
229 call setline(3, 'testing3')
230 exe '1,3yank +'
231
232 call assert_equal("testing\ntesting2\ntesting3\n", system('wl-paste -n'))
233
234 bw!
235
236 " Primary selection
237 new
238
239 call setline(1, 'testing')
240 yank *
241
242 call assert_equal("testing\n", system('wl-paste -p -n'))
243
244 call setline(2, 'testing2')
245 call setline(3, 'testing3')
246 exe '1,3yank *'
247
248 call assert_equal("testing\ntesting2\ntesting3\n", system('wl-paste -p -n'))
249
250 bw!
251endfunc
252
253
254" Check if correct mime types are advertised when we own the selection
255func Test_wayland_mime_types_correct()
256 call s:PreTest()
257
258 let l:mimes = [
259 \ '_VIMENC_TEXT',
260 \ '_VIM_TEXT',
261 \ 'text/plain;charset=utf-8',
262 \ 'text/plain',
263 \ 'UTF8_STRING',
264 \ 'STRING',
265 \ 'TEXT'
266 \ ]
267
268 call setreg('+', 'text', 'c')
269
270 for mime in split(system('wl-paste -l'), "\n")
271 if index(l:mimes, mime) == -1
272 call assert_report("'" .. mime .. "' is not a supported mime type")
273 endif
274 endfor
275
276 call setreg('*', 'text', 'c')
277
278 for mime in split(system('wl-paste -p -l'), "\n")
279 if index(l:mimes, mime) == -1
280 call assert_report("'" .. mime .. "' is not a supported mime type")
281 endif
282 endfor
283endfunc
284
285" Test if the _VIM_TEXT and _VIMENC_TEXT formats are correct:
286" _VIM_TEXT: preserves motion type (line/char/block wise)
287" _VIMENC_TEXT: same but also indicates the encoding type
288func Test_wayland_paste_vim_format_correct()
289 call s:PreTest()
290
291 " Vim doesn't support null characters in strings, so we use the -v flag of the
292 " cat program to show them in a printable way, if it is available.
293 call system("cat -v")
294 if v:shell_error != 0
295 throw 'Skipped: cat program does not have -v command-line flag'
296 endif
297
298 set encoding=utf-8
299
300 let l:GetSel = {type -> system('wl-paste -t ' .. type .. ' | cat -v')}
301 let l:GetSelP = {type -> system('wl-paste -p -t ' .. type .. ' | cat -v')}
302
303 " Regular selection
304 call setreg('+', 'text', 'c')
305 call assert_equal("^@text", l:GetSel('_VIM_TEXT'))
306 call setreg('+', 'text', 'c')
307 call assert_equal("^@utf-8^@text", l:GetSel('_VIMENC_TEXT'))
308
309 call setreg('+', 'text', 'l')
310 call assert_equal("^Atext\n", l:GetSel('_VIM_TEXT'))
311 call setreg('+', 'text', 'l')
312 call assert_equal("^Autf-8^@text\n",l:GetSel('_VIMENC_TEXT'))
313
314 call setreg('+', 'text', 'b')
315 call assert_equal("^Btext\n", l:GetSel('_VIM_TEXT'))
316 call setreg('+', 'text', 'b')
317 call assert_equal("^Butf-8^@text\n", l:GetSel('_VIMENC_TEXT'))
318
319 " Primary selection
320 call setreg('*', 'text', 'c')
321 call assert_equal("^@text", l:GetSelP('_VIM_TEXT'))
322 call setreg('*', 'text', 'c')
323 call assert_equal("^@utf-8^@text", l:GetSelP('_VIMENC_TEXT'))
324
325 call setreg('*', 'text', 'l')
326 call assert_equal("^Atext\n", l:GetSelP('_VIM_TEXT'))
327 call setreg('*', 'text', 'l')
328 call assert_equal("^Autf-8^@text\n",l:GetSelP('_VIMENC_TEXT'))
329
330 call setreg('*', 'text', 'b')
331 call assert_equal("^Btext\n", l:GetSelP('_VIM_TEXT'))
332 call setreg('*', 'text', 'b')
333 call assert_equal("^Butf-8^@text\n", l:GetSelP('_VIMENC_TEXT'))
334
335 set encoding&
336endfunc
337
338" Test checking if * and + registers are not the same
339func Test_wayland_plus_star_not_same()
340 call s:PreTest()
341 new
342
343 call system('wl-copy "regular"')
344 call system('wl-copy -p "primary"')
345
346 call assert_notequal(getreg('+'), getreg('*'))
347
348 " Check if when we are the source client
349 call setreg('+', 'REGULAR')
350 call setreg('*', 'PRIMARY')
351
352 call assert_notequal(system('wl-paste -p'), system('wl-paste'))
353
354 bw!
355endfunc
356
Hirohito Higashi73b96502025-06-28 18:18:21 +0200357" Test if autoselect option in 'clipboard' works properly for Wayland
Foxe Chenb90c2392025-06-27 21:10:35 +0200358func Test_wayland_autoselect_works()
359 call s:PreTest()
360 call s:CheckXConnection()
361
362 let l:lines =<< trim END
363 set cpm=wayland
364 set clipboard=autoselect
365
366 new
367 call setline(1, 'LINE 1')
368 call setline(2, 'LINE 2')
369 call setline(3, 'LINE 3')
370
371 call cursor(1, 1)
372 END
373
374 call writefile(l:lines, 'Wltester', 'D')
375
376 let l:name = 'WLVIMTEST'
377 let l:cmd = GetVimCommand() .. ' -S Wltester --servername ' .. l:name
378 let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
379
380 call WaitForAssert({-> assert_equal("run", job_status(l:job))})
381 call WaitForAssert({-> assert_match(name, serverlist())})
382 1
383 call remote_send(l:name, "ve")
384 call WaitForAssert({-> assert_equal('LINE', system('wl-paste -p -n'))})
385
386 call remote_send(l:name, "w")
387 call WaitForAssert({-> assert_equal('LINE 1', system('wl-paste -p -n'))})
388
389 call remote_send(l:name, "V")
390 call WaitForAssert({-> assert_equal("LINE 1\n", system('wl-paste -p -n'))})
391
392 " Reset cursor
393 call remote_send(l:name, "\<Esc>:call cursor(1, 1)\<CR>")
394 call WaitForAssert({-> assert_equal("LINE 1\n", system('wl-paste -p -n'))})
395
396 " Test visual block mode
397 call remote_send(l:name, "\<C-q>jjj") " \<C-v> doesn't seem to work but \<C-q>
398 " does...
399
400 call WaitForAssert({-> assert_equal("L\nL\nL\n", system('wl-paste -p -n'))})
401
402 eval remote_send(l:name, "\<Esc>:qa!\<CR>")
403
404 try
405 call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
406 finally
407 if job_status(l:job) != 'dead'
408 call assert_report('Vim instance did not exit')
409 call job_stop(l:job, 'kill')
410 endif
411 endtry
412endfunc
413
414" Check if the -Y flag works properly
415func Test_no_wayland_connect_cmd_flag()
416 call s:PreTest()
417 call s:CheckXConnection()
418
419 let l:name = 'WLFLAGVIMTEST'
420 let l:cmd = GetVimCommand() .. ' -Y --servername ' .. l:name
421 let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
422
423 call WaitForAssert({-> assert_equal("run", job_status(l:job))})
424 call WaitForAssert({-> assert_match(name, serverlist())})
425
426 call WaitForAssert({->assert_equal('',
427 \ remote_expr(l:name, 'v:wayland_display'))})
428
429 call remote_send(l:name, ":wlrestore\<CR>")
430 call WaitForAssert({-> assert_equal('',
431 \ remote_expr(l:name, 'v:wayland_display'))})
432
433 call remote_send(l:name, ":wlrestore " .. $WAYLAND_DISPLAY .. "\<CR>")
434 call WaitForAssert({-> assert_equal('',
435 \ remote_expr(l:name, 'v:wayland_display'))})
436
437 call remote_send(l:name, ":wlrestore IDONTEXIST\<CR>")
438 call WaitForAssert({-> assert_equal('',
439 \ remote_expr(l:name, 'v:wayland_display'))})
440
441 eval remote_send(l:name, "\<Esc>:qa!\<CR>")
442 try
443 call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
444 finally
445 if job_status(l:job) != 'dead'
446 call assert_report('Vim instance did not exit')
447 call job_stop(l:job, 'kill')
448 endif
449 endtry
450endfunc
451
452" Test behaviour when we do something like suspend Vim
453func Test_wayland_become_inactive()
454 call s:PreTest()
455 call s:CheckXConnection()
456
457 let l:name = 'WLLOSEVIMTEST'
458 let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
459 let l:job = job_start(cmd, {
460 \ 'stoponexit': 'kill',
461 \ 'out_io': 'null',
462 \ })
463
464 call WaitForAssert({-> assert_equal("run", job_status(l:job))})
465 call WaitForAssert({-> assert_match(name, serverlist())})
466
467 call remote_send(l:name, "iSOME TEXT\<Esc>\"+yy")
468
469 call WaitForAssert({-> assert_equal("SOME TEXT\n",
470 \ system('wl-paste -n'))})
471
472 call remote_send(l:name, "\<C-z>")
473
474 call WaitForAssert({-> assert_equal("Nothing is copied\n",
475 \ system('wl-paste -n'))})
476
477 eval remote_send(l:name, "\<Esc>:qa!\<CR>")
478 try
479 call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
480 finally
481 if job_status(l:job) != 'dead'
482 call assert_report('Vim instance did not exit')
483 call job_stop(l:job, 'kill')
484 endif
485 endtry
486endfunc
487
488" Test wlseat option
489func Test_wayland_seat()
490 call s:PreTest()
491
492 " Don't know a way to create a virtual seat so just test using the existing
493 " one only
494 set wlseat=
495
496 call system('wl-copy "TESTING"')
497 call assert_equal('TESTING', getreg('+'))
498
499 set wlseat=UNKNOWN
500
501 call assert_equal('', getreg('+'))
502
503 set wlseat=idontexist
504
505 call assert_equal('', getreg('+'))
506
507 set wlseat=
508
509 call assert_equal('TESTING', getreg('+'))
510
511 set wlseat&
512endfunc
513
514" Test focus stealing
515func Test_wayland_focus_steal()
516 call s:PreTest()
517 call s:SetupFocusStealing()
518
519 call system('wl-copy regular')
520
521 call assert_equal('regular', getreg('+'))
522
523 call system('wl-copy -p primary')
524
525 call assert_equal('primary', getreg('*'))
526
527 call setreg('+', 'REGULAR')
528
529 call assert_equal('REGULAR', system('wl-paste -n'))
530
531 call setreg('*', 'PRIMARY')
532
533 call assert_equal('PRIMARY', system('wl-paste -p -n'))
534
535 call s:UnsetupFocusStealing()
536endfunc
537
538" Test when environment is not suitable for Wayland
539func Test_wayland_bad_environment()
540 call s:PreTest()
541 call s:CheckXConnection()
542
543 unlet $WAYLAND_DISPLAY
544
545 let l:old = $XDG_RUNTIME_DIR
546 unlet $XDG_RUNTIME_DIR
547
548 let l:name = 'WLVIMTEST'
549 let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
550 let l:job = job_start(cmd, {
551 \ 'stoponexit': 'kill',
552 \ 'out_io': 'null',
553 \ })
554
555 call WaitForAssert({-> assert_equal("run", job_status(l:job))})
556 call WaitForAssert({-> assert_match(name, serverlist())})
557
558 call WaitForAssert({-> assert_equal('',
559 \ remote_expr(l:name, 'v:wayland_display'))})
560
561 eval remote_send(l:name, "\<Esc>:qa!\<CR>")
562 try
563 call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
564 finally
565 if job_status(l:job) != 'dead'
566 call assert_report('Vim instance did not exit')
567 call job_stop(l:job, 'kill')
568 endif
569 endtry
570
571 let $XDG_RUNTIME_DIR = l:old
572endfunc
573
574" Test if Vim still works properly after losing the selection
575func Test_wayland_lost_selection()
576 call s:PreTest()
577
578 call setreg('+', 'regular')
579 call setreg('*', 'primary')
580
581 call assert_equal('regular', getreg('+'))
582 call assert_equal('primary', getreg('*'))
583
584 call system('wl-copy overwrite')
585 call system('wl-copy -p overwrite')
586
587 call assert_equal('overwrite', getreg('+'))
588 call assert_equal('overwrite', getreg('*'))
589
590 call setreg('+', 'regular')
591 call setreg('*', 'primary')
592
593 call assert_equal('regular', getreg('+'))
594 call assert_equal('primary', getreg('*'))
595
596 " Test focus stealing
597 call s:SetupFocusStealing()
598
599 call setreg('+', 'regular')
600 call setreg('*', 'primary')
601
602 call assert_equal('regular', getreg('+'))
603 call assert_equal('primary', getreg('*'))
604
605 call system('wl-copy overwrite')
606 call system('wl-copy -p overwrite')
607
608 call assert_equal('overwrite', getreg('+'))
609 call assert_equal('overwrite', getreg('*'))
610
611 call setreg('+', 'regular')
612 call setreg('*', 'primary')
613
614 call assert_equal('regular', getreg('+'))
615 call assert_equal('primary', getreg('*'))
616
617 call s:UnsetupFocusStealing()
618endfunc
619
620" Test when there are no supported mime types for the selecftion
621func Test_wayland_no_mime_types_supported()
622 call s:PreTest()
623
624 wlrestore!
625
626 call system('wl-copy -t image/png testing')
627
628 call assert_equal('', getreg('+'))
629 call assert_fails('put +', 'E353:')
630endfunc
631
632" Test behaviour with large selections in terms of data size
633func Test_wayland_handle_large_data()
634 call s:PreTest()
635
636 call writefile([''], 'data_file', 'D')
637 call writefile([''], 'data_file_cmp', 'D')
638 call system('yes c | head -5000000 > data_file') " ~ 10 MB
639 call system('wl-copy -t TEXT < data_file')
640
641 edit data_file_cmp
642
643 put! +
644
645 write
646
647 call system('truncate -s -1 data_file_cmp') " Remove newline at the end
648 call system('cmp --silent data_file data_file_cmp')
649 call assert_equal(0, v:shell_error)
650
651 " copy the text
652 call feedkeys('gg0v$G"+yy', 'x')
653 call system('wl-paste -n -t TEXT > data_file')
654
655 call system('cmp --silent data_file data_file_cmp')
656 call assert_equal(0, v:shell_error)
657
658 bw!
659endfunc
660
661" vim: shiftwidth=2 sts=2 expandtab