mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	vim-patch:9.1.0797: testing of options can be further improved (#30893)
Problem:  testing of options can be further improved
Solution: split the generated option test into test_options_all.vim,
          add more test cases, save and restore values, fix use-after-free
closes: vim/vim#15894
6eca04e9f1
Co-authored-by: Milly <milly.ca@gmail.com>
			
			
This commit is contained in:
		| @@ -1417,7 +1417,7 @@ A jump table for the options with a short description can be found at |Q_op|. | |||||||
|  |  | ||||||
| 						*'cmdheight'* *'ch'* | 						*'cmdheight'* *'ch'* | ||||||
| 'cmdheight' 'ch'	number	(default 1) | 'cmdheight' 'ch'	number	(default 1) | ||||||
| 			global or local to tab page |global-local| | 			global or local to tab page | ||||||
| 	Number of screen lines to use for the command-line.  Helps avoiding | 	Number of screen lines to use for the command-line.  Helps avoiding | ||||||
| 	|hit-enter| prompts. | 	|hit-enter| prompts. | ||||||
| 	The value of this option is stored with the tab page, so that each tab | 	The value of this option is stored with the tab page, so that each tab | ||||||
|   | |||||||
| @@ -670,7 +670,7 @@ local function scope_to_doc(s) | |||||||
|     return m[s[1]] |     return m[s[1]] | ||||||
|   end |   end | ||||||
|   assert(s[1] == 'global') |   assert(s[1] == 'global') | ||||||
|   return 'global or ' .. m[s[2]] .. ' |global-local|' |   return 'global or ' .. m[s[2]] .. (s[2] ~= 'tab' and ' |global-local|' or '') | ||||||
| end | end | ||||||
|  |  | ||||||
| -- @param o vim.option_meta | -- @param o vim.option_meta | ||||||
|   | |||||||
| @@ -777,7 +777,11 @@ void getout(int exitval) | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (p_shada && *p_shada != NUL) { |   if ( | ||||||
|  | #ifdef EXITFREE | ||||||
|  |       !entered_free_all_mem && | ||||||
|  | #endif | ||||||
|  |       p_shada && *p_shada != NUL) { | ||||||
|     // Write out the registers, history, marks etc, to the ShaDa file |     // Write out the registers, history, marks etc, to the ShaDa file | ||||||
|     shada_write_file(NULL, false); |     shada_write_file(NULL, false); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -154,7 +154,7 @@ newtests: newtestssilent | |||||||
|  |  | ||||||
| newtestssilent: $(NEW_TESTS_RES) | newtestssilent: $(NEW_TESTS_RES) | ||||||
|  |  | ||||||
| GEN_OPT_DEPS = gen_opt_test.vim ../../../src/nvim/options.lua | GEN_OPT_DEPS = gen_opt_test.vim ../../../src/nvim/options.lua ../../../runtime/doc/options.txt | ||||||
|  |  | ||||||
| opt_test.vim: $(GEN_OPT_DEPS) | opt_test.vim: $(GEN_OPT_DEPS) | ||||||
| 	$(NVIM_PRG) -e -s -u NONE $(NO_INITS) -S $(GEN_OPT_DEPS) | 	$(NVIM_PRG) -e -s -u NONE $(NO_INITS) -S $(GEN_OPT_DEPS) | ||||||
| @@ -164,7 +164,7 @@ opt_test.vim: $(GEN_OPT_DEPS) | |||||||
| 	fi | 	fi | ||||||
|  |  | ||||||
| # Explicit dependencies. | # Explicit dependencies. | ||||||
| test_options.res test_alot.res: opt_test.vim | test_options_all.res: opt_test.vim | ||||||
|  |  | ||||||
| %.res: %.vim .gdbinit | %.res: %.vim .gdbinit | ||||||
| 	@echo "[OLDTEST] Running" $* | 	@echo "[OLDTEST] Running" $* | ||||||
|   | |||||||
| @@ -1,4 +1,10 @@ | |||||||
| " Script to generate testdir/opt_test.vim from options.lua | " Script to generate test/old/testdir/opt_test.vim from src/nvim/options.lua | ||||||
|  | " and runtime/doc/options.txt | ||||||
|  |  | ||||||
|  | set cpo&vim | ||||||
|  |  | ||||||
|  | " Only do this when build with the +eval feature. | ||||||
|  | if 1 | ||||||
|  |  | ||||||
| try | try | ||||||
|  |  | ||||||
| @@ -6,13 +12,64 @@ set nomore | |||||||
|  |  | ||||||
| const K_KENTER = -16715 | const K_KENTER = -16715 | ||||||
|  |  | ||||||
|  | " Get global-local options. | ||||||
|  | " "key" is full-name of the option. | ||||||
|  | " "value" is the local value to switch back to the global value. | ||||||
|  | b options.txt | ||||||
|  | call cursor(1, 1) | ||||||
|  | let global_locals = {} | ||||||
|  | while search("^'[^']*'.*\\n.*|global-local", 'W') | ||||||
|  |   let fullname = getline('.')->matchstr("^'\\zs[^']*") | ||||||
|  |   let global_locals[fullname] = '' | ||||||
|  | endwhile | ||||||
|  | call extend(global_locals, #{ | ||||||
|  |       \ scrolloff: -1, | ||||||
|  |       \ sidescrolloff: -1, | ||||||
|  |       \ undolevels: -12345, | ||||||
|  |       \}) | ||||||
|  |  | ||||||
|  | " Get local-noglobal options. | ||||||
|  | " "key" is full-name of the option. | ||||||
|  | " "value" is no used. | ||||||
|  | b options.txt | ||||||
|  | call cursor(1, 1) | ||||||
|  | let local_noglobals = {} | ||||||
|  | while search("^'[^']*'.*\\n.*|local-noglobal", 'W') | ||||||
|  |   let fullname = getline('.')->matchstr("^'\\zs[^']*") | ||||||
|  |   let local_noglobals[fullname] = v:true | ||||||
|  | endwhile | ||||||
|  |  | ||||||
|  | " Options to skip `setglobal` tests. | ||||||
|  | " "key" is full-name of the option. | ||||||
|  | " "value" is the reason. | ||||||
|  | let skip_setglobal_reasons = #{ | ||||||
|  |       \ iminsert: 'The global value is always overwritten by the local value', | ||||||
|  |       \ imsearch: 'The global value is always overwritten by the local value', | ||||||
|  |       \ breakindentopt:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ colorcolumn:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ conceallevel:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ foldcolumn:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ foldmethod:	'TODO: fix `setglobal fdm=` not given an error', | ||||||
|  |       \ iskeyword:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ numberwidth:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ scrolloff:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ shiftwidth:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ sidescrolloff:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ signcolumn:	'TODO(nvim): fix missing error handling for setglobal', | ||||||
|  |       \ spelloptions:	'TODO(nvim): fix missing error handling for setglobal', | ||||||
|  |       \ tabstop:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ termwinkey:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ termwinsize:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ textwidth:	'TODO: fix missing error handling for setglobal', | ||||||
|  |       \ winhighlight:	'TODO(nvim): fix missing error handling for setglobal', | ||||||
|  |       \} | ||||||
|  |  | ||||||
| " The terminal size is restored at the end. | " The terminal size is restored at the end. | ||||||
| let script = [ | let script = [ | ||||||
|       \ '" DO NOT EDIT: Generated with gen_opt_test.vim', |       \ '" DO NOT EDIT: Generated with gen_opt_test.vim', | ||||||
|       \ '" Used by test_options.vim.', |       \ '" Used by test_options_all.vim.', | ||||||
|       \ '', |       \ '', | ||||||
|       \ 'let save_columns = &columns', |       \ 'scriptencoding utf-8', | ||||||
|       \ 'let save_lines = &lines', |  | ||||||
|       \ ] |       \ ] | ||||||
|  |  | ||||||
| let options = luaeval('loadfile("../../../src/nvim/options.lua")().options') | let options = luaeval('loadfile("../../../src/nvim/options.lua")().options') | ||||||
| @@ -314,6 +371,27 @@ let test_values = { | |||||||
|       \ 'otherstring': [['', 'xxx'], []], |       \ 'otherstring': [['', 'xxx'], []], | ||||||
|       \} |       \} | ||||||
|  |  | ||||||
|  | " Two lists with values: values that pre- and post-processing in test. | ||||||
|  | " Clear out t_WS: we don't want to resize the actual terminal. | ||||||
|  | let test_prepost = { | ||||||
|  |       \ 'browsedir': [["call mkdir('Xdir with space', 'D')"], []], | ||||||
|  |       \ 'columns': [[ | ||||||
|  |       \		'set t_WS=', | ||||||
|  |       \		'let save_columns = &columns' | ||||||
|  |       \		], [ | ||||||
|  |       \		'let &columns = save_columns', | ||||||
|  |       \		'set t_WS&' | ||||||
|  |       \		]], | ||||||
|  |       \ 'lines': [[ | ||||||
|  |       \		'set t_WS=', | ||||||
|  |       \		'let save_lines = &lines' | ||||||
|  |       \		], [ | ||||||
|  |       \		'let &lines = save_lines', | ||||||
|  |       \		'set t_WS&' | ||||||
|  |       \		]], | ||||||
|  |       \ 'verbosefile': [[], ['call delete("Xfile")']], | ||||||
|  |       \} | ||||||
|  |  | ||||||
| const invalid_options = test_values->keys() | const invalid_options = test_values->keys() | ||||||
|       \->filter({-> v:val !~# '^other' && !exists($"&{v:val}")}) |       \->filter({-> v:val !~# '^other' && !exists($"&{v:val}")}) | ||||||
| if !empty(invalid_options) | if !empty(invalid_options) | ||||||
| @@ -321,70 +399,103 @@ if !empty(invalid_options) | |||||||
| endif | endif | ||||||
|  |  | ||||||
| for option in options | for option in options | ||||||
|   let name = option.full_name |   let fullname = option.full_name | ||||||
|   let shortname = get(option, 'abbreviation', name) |   let shortname = get(option, 'abbreviation', fullname) | ||||||
|  |  | ||||||
|   if get(option, 'immutable', v:false) |   if get(option, 'immutable', v:false) | ||||||
|     continue |     continue | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|   if has_key(test_values, name) |   let [valid_values, invalid_values] = test_values[ | ||||||
|     let a = test_values[name] | 	\ has_key(test_values, fullname) ? fullname | ||||||
|   elseif option.type == 'number' | 	\ : option.type == 'number' ? 'othernum' | ||||||
|     let a = test_values['othernum'] | 	\ : 'otherstring'] | ||||||
|   else |  | ||||||
|     let a = test_values['otherstring'] |   if empty(valid_values) && empty(invalid_values) | ||||||
|  |     continue | ||||||
|   endif |   endif | ||||||
|   if len(a[0]) > 0 || len(a[1]) > 0 |  | ||||||
|     if name == 'browsedir' |  | ||||||
|       call add(script, 'call mkdir("Xdir with space")') |  | ||||||
|     endif |  | ||||||
|  |  | ||||||
|     if option.type == 'boolean' |   call add(script, $"func Test_opt_set_{fullname}()") | ||||||
|       call add(script, 'set ' . name) |   call add(script, $"if exists('+{fullname}') && execute('set!') =~# '\\n..{fullname}\\([=\\n]\\|$\\)'") | ||||||
|       call add(script, 'set ' . shortname) |   call add(script, $"let l:saved = [&g:{fullname}, &l:{fullname}]") | ||||||
|       call add(script, 'set no' . name) |   call add(script, 'endif') | ||||||
|       call add(script, 'set no' . shortname) |  | ||||||
|     else |   let [pre_processing, post_processing] = get(test_prepost, fullname, [[], []]) | ||||||
|       for val in a[0] |   let script += pre_processing | ||||||
| 	call add(script, 'set ' . name . '=' . val) |  | ||||||
| 	call add(script, 'set ' . shortname . '=' . val) |   if option.type == 'boolean' | ||||||
|  |     for opt in [fullname, shortname] | ||||||
|  |       for cmd in ['set', 'setlocal', 'setglobal'] | ||||||
|  | 	call add(script, $'{cmd} {opt}') | ||||||
|  | 	call add(script, $'{cmd} no{opt}') | ||||||
|  | 	call add(script, $'{cmd} inv{opt}') | ||||||
|  | 	call add(script, $'{cmd} {opt}!') | ||||||
|       endfor |       endfor | ||||||
|  |     endfor | ||||||
|       " setting an option can only fail when it's implemented. |   else  " P_NUM || P_STRING | ||||||
|       call add(script, "if exists('+" . name . "')") |     " Normal tests | ||||||
|       for val in a[1] |     for opt in [fullname, shortname] | ||||||
| 	call add(script, "silent! call assert_fails('set " . name . "=" . val . "')") |       for cmd in ['set', 'setlocal', 'setglobal'] | ||||||
| 	call add(script, "silent! call assert_fails('set " . shortname . "=" . val . "')") | 	for val in valid_values | ||||||
|  | 	  if local_noglobals->has_key(fullname) && cmd ==# 'setglobal' | ||||||
|  | 	    " Skip `:setglobal {option}={val}` for local-noglobal option. | ||||||
|  | 	    " It has no effect. | ||||||
|  | 	    let pre = '" Skip local-noglobal: ' | ||||||
|  | 	  else | ||||||
|  | 	    let pre = '' | ||||||
|  | 	  endif | ||||||
|  | 	  call add(script, $'{pre}{cmd} {opt}={val}') | ||||||
|  | 	endfor | ||||||
|       endfor |       endfor | ||||||
|       call add(script, "endif") |       " Testing to clear the local value and switch back to the global value. | ||||||
|     endif |       if global_locals->has_key(fullname) | ||||||
|  | 	let swichback_val = global_locals[fullname] | ||||||
|  | 	call add(script, $'setlocal {opt}={swichback_val}') | ||||||
|  |       endif | ||||||
|  |     endfor | ||||||
|  |  | ||||||
|     " cannot change 'termencoding' in GTK |     " Failure tests | ||||||
|     if name != 'termencoding' || !has('gui_gtk') |     " Setting an option can only fail when it's implemented. | ||||||
|       call add(script, 'set ' . name . '&') |     call add(script, $"if exists('+{fullname}')") | ||||||
|       call add(script, 'set ' . shortname . '&') |     for opt in [fullname, shortname] | ||||||
|     endif |       for cmd in ['set', 'setlocal', 'setglobal'] | ||||||
|     if name == 'browsedir' | 	for val in invalid_values | ||||||
|       call add(script, 'call delete("Xdir with space", "d")') | 	  if val is# global_locals->get(fullname, {}) && cmd ==# 'setlocal' | ||||||
|     elseif name == 'verbosefile' | 	    " Skip setlocal switchback-value to global-local option. It will | ||||||
|       call add(script, 'call delete("Xfile")') | 	    " not result in failure. | ||||||
|     endif | 	    let pre = '" Skip global-local: ' | ||||||
|  | 	  elseif local_noglobals->has_key(fullname) && cmd ==# 'setglobal' | ||||||
|     if name == 'more' | 	    " Skip setglobal to local-noglobal option. It will not result in | ||||||
|       call add(script, 'set nomore') | 	    " failure. | ||||||
|     elseif name == 'laststatus' | 	    let pre = '" Skip local-noglobal: ' | ||||||
|       call add(script, 'set laststatus=1') | 	  elseif skip_setglobal_reasons->has_key(fullname) && cmd ==# 'setglobal' | ||||||
|     elseif name == 'lines' | 	    " Skip setglobal to reasoned option. It will not result in failure. | ||||||
|       call add(script, 'let &lines = save_lines') | 	    let reason = skip_setglobal_reasons[fullname] | ||||||
|     endif | 	    let pre = $'" Skip {reason}: ' | ||||||
|  | 	  else | ||||||
|  | 	    let pre = '' | ||||||
|  | 	  endif | ||||||
|  | 	  let cmdline = $'{cmd} {opt}={val}' | ||||||
|  | 	  call add(script, $"{pre}silent! call assert_fails({string(cmdline)})") | ||||||
|  | 	endfor | ||||||
|  |       endfor | ||||||
|  |     endfor | ||||||
|  |     call add(script, "endif") | ||||||
|   endif |   endif | ||||||
|  |  | ||||||
|  |   " Cannot change 'termencoding' in GTK | ||||||
|  |   if fullname != 'termencoding' || !has('gui_gtk') | ||||||
|  |     call add(script, $'set {fullname}&') | ||||||
|  |     call add(script, $'set {shortname}&') | ||||||
|  |     call add(script, $"if exists('l:saved')") | ||||||
|  |     call add(script, $"let [&g:{fullname}, &l:{fullname}] = l:saved") | ||||||
|  |     call add(script, 'endif') | ||||||
|  |   endif | ||||||
|  |  | ||||||
|  |   let script += post_processing | ||||||
|  |   call add(script, 'endfunc') | ||||||
| endfor | endfor | ||||||
|  |  | ||||||
| call add(script, 'let &columns = save_columns') |  | ||||||
| call add(script, 'let &lines = save_lines') |  | ||||||
| call add(script, 'source unix.vim') |  | ||||||
|  |  | ||||||
| call writefile(script, 'opt_test.vim') | call writefile(script, 'opt_test.vim') | ||||||
|  |  | ||||||
| " Write error messages if error occurs. | " Write error messages if error occurs. | ||||||
| @@ -397,4 +508,8 @@ catch | |||||||
|   write |   write | ||||||
| endtry | endtry | ||||||
|  |  | ||||||
|  | endif | ||||||
|  |  | ||||||
| qa! | qa! | ||||||
|  |  | ||||||
|  | " vim:sw=2:ts=8:noet:nolist:nosta: | ||||||
|   | |||||||
| @@ -1,8 +1,11 @@ | |||||||
| " Test for options | " Test for options | ||||||
|  |  | ||||||
|  | source shared.vim | ||||||
| source check.vim | source check.vim | ||||||
| source view_util.vim | source view_util.vim | ||||||
|  |  | ||||||
|  | scriptencoding utf-8 | ||||||
|  |  | ||||||
| func Test_whichwrap() | func Test_whichwrap() | ||||||
|   set whichwrap=b,s |   set whichwrap=b,s | ||||||
|   call assert_equal('b,s', &whichwrap) |   call assert_equal('b,s', &whichwrap) | ||||||
| @@ -1024,15 +1027,6 @@ func Test_set_all_one_column() | |||||||
|   call assert_equal(sort(copy(options)), options) |   call assert_equal(sort(copy(options)), options) | ||||||
| endfunc | endfunc | ||||||
|  |  | ||||||
| func Test_set_values() |  | ||||||
|   " opt_test.vim is generated from ../optiondefs.h using gen_opt_test.vim |  | ||||||
|   if filereadable('opt_test.vim') |  | ||||||
|     source opt_test.vim |  | ||||||
|   else |  | ||||||
|     throw 'Skipped: opt_test.vim does not exist' |  | ||||||
|   endif |  | ||||||
| endfunc |  | ||||||
|  |  | ||||||
| func Test_renderoptions() | func Test_renderoptions() | ||||||
|   throw 'skipped: Nvim does not support renderoptions' |   throw 'skipped: Nvim does not support renderoptions' | ||||||
|   " Only do this for Windows Vista and later, fails on Windows XP and earlier. |   " Only do this for Windows Vista and later, fails on Windows XP and earlier. | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								test/old/testdir/test_options_all.vim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								test/old/testdir/test_options_all.vim
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | " Test for options | ||||||
|  |  | ||||||
|  | " opt_test.vim is generated from src/optiondefs.h and runtime/doc/options.txt | ||||||
|  | " using gen_opt_test.vim | ||||||
|  | if filereadable('opt_test.vim') | ||||||
|  |   source opt_test.vim | ||||||
|  | else | ||||||
|  |   func Test_set_values() | ||||||
|  |     throw 'Skipped: opt_test.vim does not exist' | ||||||
|  |   endfunc | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | " vim: shiftwidth=2 sts=2 expandtab | ||||||
		Reference in New Issue
	
	Block a user
	 zeertzjq
					zeertzjq