patch 9.1.0797: testing of options can be further improved
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: #15894
Signed-off-by: Milly <milly.ca@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/main.c b/src/main.c
index e5faaa7..ecc61f4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1694,7 +1694,11 @@
}
#ifdef FEAT_VIMINFO
- if (*p_viminfo != NUL)
+ if (
+# ifdef EXITFREE
+ entered_free_all_mem == FALSE &&
+# endif
+ *p_viminfo != NUL)
// Write out the registers, history, marks etc, to the viminfo file
write_viminfo(NULL, FALSE);
#endif
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 67ef641..750c194 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -232,6 +232,7 @@
test_normal \
test_number \
test_options \
+ test_options_all \
test_packadd \
test_partial \
test_paste \
@@ -492,6 +493,7 @@
test_normal.res \
test_number.res \
test_options.res \
+ test_options_all.res \
test_packadd.res \
test_partial.res \
test_paste.res \
diff --git a/src/testdir/Make_ming.mak b/src/testdir/Make_ming.mak
index 16632b3..2ffe402 100644
--- a/src/testdir/Make_ming.mak
+++ b/src/testdir/Make_ming.mak
@@ -22,7 +22,7 @@
include Make_all.mak
# Explicit dependencies.
-test_options.res test_alot.res: opt_test.vim
+test_options_all.res: opt_test.vim
TEST_OUTFILES = $(SCRIPTS_TINY_OUT)
DOSTMP = dostmp
@@ -157,7 +157,7 @@
$(VIMPROG) -u gui_preinit.vim -U gui_init.vim $(NO_PLUGINS) -S runtest.vim $<
@$(DEL) vimcmd
-opt_test.vim: gen_opt_test.vim ../optiondefs.h
+opt_test.vim: gen_opt_test.vim ../optiondefs.h ../../runtime/doc/options.txt
$(VIMPROG) -e -s -u NONE $(COMMON_ARGS) --nofork -S $^
@if test -f test.log; then \
cat test.log; \
diff --git a/src/testdir/Make_mvc.mak b/src/testdir/Make_mvc.mak
index bbfd8f0..1a54823 100644
--- a/src/testdir/Make_mvc.mak
+++ b/src/testdir/Make_mvc.mak
@@ -16,7 +16,7 @@
!include Make_all.mak
# Explicit dependencies.
-test_options.res test_alot.res: opt_test.vim
+test_options_all.res: opt_test.vim
TEST_OUTFILES = $(SCRIPTS_TINY_OUT)
DOSTMP = dostmp
@@ -151,7 +151,7 @@
$(VIMPROG) -u gui_preinit.vim -U gui_init.vim $(NO_PLUGINS) -S runtest.vim $*.vim
@del vimcmd
-opt_test.vim: gen_opt_test.vim ../optiondefs.h
+opt_test.vim: gen_opt_test.vim ../optiondefs.h ../../runtime/doc/options.txt
$(VIMPROG) -e -s -u NONE $(COMMON_ARGS) --nofork -S $**
@if exist test.log ( type test.log & exit /b 1 )
diff --git a/src/testdir/Makefile b/src/testdir/Makefile
index 66b8f1b..3b665e7 100644
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -30,7 +30,7 @@
include Make_all.mak
# Explicit dependencies.
-test_options.res test_alot.res: opt_test.vim
+test_options_all.res: opt_test.vim
.SUFFIXES: .in .out .res .vim
@@ -160,7 +160,7 @@
$(RUN_VIMTEST) -u gui_preinit.vim -U gui_init.vim $(NO_PLUGINS) -S runtest.vim $<
@rm vimcmd
-GEN_OPT_DEPS = gen_opt_test.vim ../optiondefs.h
+GEN_OPT_DEPS = gen_opt_test.vim ../optiondefs.h ../../runtime/doc/options.txt
opt_test.vim: $(GEN_OPT_DEPS)
$(VIMPROG) -e -s -u NONE $(NO_INITS) --nofork --gui-dialog-file guidialog -S $(GEN_OPT_DEPS)
diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim
index f308e0e..e855602 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -1,4 +1,5 @@
-" Script to generate testdir/opt_test.vim from optiondefs.h
+" Script to generate src/testdir/opt_test.vim from src/optiondefs.h and
+" runtime/doc/options.txt
set cpo=&vim
@@ -11,19 +12,65 @@
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',
+ \ 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',
+ \}
+
" The terminal size is restored at the end.
-" Clear out t_WS, we don't want to resize the actual terminal.
let script = [
\ '" DO NOT EDIT: Generated with gen_opt_test.vim',
- \ '" Used by test_options.vim.',
+ \ '" Used by test_options_all.vim.',
\ '',
- \ 'let save_columns = &columns',
- \ 'let save_lines = &lines',
- \ 'set t_WS=',
+ \ 'scriptencoding utf-8',
\ ]
-/#define p_term
-let end = line('.')
+b optiondefs.h
+const end = search('#define p_term', 'nw')
" font name that works everywhere (hopefully)
let fontname = has('win32') ? 'fixedsys' : 'fixed'
@@ -295,6 +342,27 @@
\ '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()
\->filter({-> v:val !~# '^other' && !exists($"&{v:val}")})
if !empty(invalid_options)
@@ -302,70 +370,105 @@
endif
1
-/struct vimoption options
+call search('struct vimoption options')
while 1
- /{"
- if line('.') > end
+ if search('{"', 'W') > end
break
endif
let line = getline('.')
- let name = substitute(line, '.*{"\([^"]*\)".*', '\1', '')
+ let fullname = substitute(line, '.*{"\([^"]*\)".*', '\1', '')
let shortname = substitute(line, '.*"\([^"]*\)".*', '\1', '')
- if has_key(test_values, name)
- let a = test_values[name]
- elseif line =~ 'P_NUM'
- let a = test_values['othernum']
- else
- let a = test_values['otherstring']
+ let [valid_values, invalid_values] = test_values[
+ \ has_key(test_values, fullname) ? fullname
+ \ : line =~ 'P_NUM' ? 'othernum'
+ \ : 'otherstring']
+
+ if empty(valid_values) && empty(invalid_values)
+ continue
endif
- if len(a[0]) > 0 || len(a[1]) > 0
- if name == 'browsedir'
- call add(script, 'call mkdir("Xdir with space")')
- endif
- if line =~ 'P_BOOL'
- call add(script, 'set ' . name)
- call add(script, 'set ' . shortname)
- call add(script, 'set no' . name)
- call add(script, 'set no' . shortname)
- else
- for val in a[0]
- call add(script, 'set ' . name . '=' . val)
- call add(script, 'set ' . shortname . '=' . val)
+ call add(script, $"func Test_opt_set_{fullname}()")
+ call add(script, $"if exists('+{fullname}') && execute('set!') =~# '\\n..{fullname}\\([=\\n]\\|$\\)'")
+ call add(script, $"let l:saved = [&g:{fullname}, &l:{fullname}]")
+ call add(script, 'endif')
+
+ let [pre_processing, post_processing] = get(test_prepost, fullname, [[], []])
+ let script += pre_processing
+
+ if line =~ 'P_BOOL'
+ 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
-
- " setting an option can only fail when it's implemented.
- call add(script, "if exists('+" . name . "')")
- for val in a[1]
- call add(script, "silent! call assert_fails('set " . name . "=" . val . "')")
- call add(script, "silent! call assert_fails('set " . shortname . "=" . val . "')")
+ endfor
+ else " P_NUM || P_STRING
+ " Normal tests
+ for opt in [fullname, shortname]
+ for cmd in ['set', 'setlocal', 'setglobal']
+ 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
- call add(script, "endif")
- endif
+ " Testing to clear the local value and switch back to the global value.
+ 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
- if name != 'termencoding' || !has('gui_gtk')
- call add(script, 'set ' . name . '&')
- call add(script, 'set ' . shortname . '&')
- endif
- if name == 'browsedir'
- call add(script, 'call delete("Xdir with space", "d")')
- elseif name == 'verbosefile'
- call add(script, 'call delete("Xfile")')
- endif
-
- if name == 'more'
- call add(script, 'set nomore')
- elseif name == 'lines'
- call add(script, 'let &lines = save_lines')
- endif
+ " Failure tests
+ " Setting an option can only fail when it's implemented.
+ call add(script, $"if exists('+{fullname}')")
+ for opt in [fullname, shortname]
+ for cmd in ['set', 'setlocal', 'setglobal']
+ for val in invalid_values
+ if val is# global_locals->get(fullname, {}) && cmd ==# 'setlocal'
+ " Skip setlocal switchback-value to global-local option. It will
+ " not result in failure.
+ let pre = '" Skip global-local: '
+ elseif local_noglobals->has_key(fullname) && cmd ==# 'setglobal'
+ " Skip setglobal to local-noglobal option. It will not result in
+ " failure.
+ let pre = '" Skip local-noglobal: '
+ elseif skip_setglobal_reasons->has_key(fullname) && cmd ==# 'setglobal'
+ " Skip setglobal to reasoned option. It will not result in failure.
+ let reason = skip_setglobal_reasons[fullname]
+ 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
+
+ " 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')
endwhile
-call add(script, 'let &columns = save_columns')
-call add(script, 'let &lines = save_lines')
-
call writefile(script, 'opt_test.vim')
" Write error messages if error occurs.
@@ -381,3 +484,5 @@
endif
qa!
+
+" vim:sw=2:ts=8:noet:nolist:nosta:
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index eec71ef..cbc84a4 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -4,6 +4,8 @@
source check.vim
source view_util.vim
+scriptencoding utf-8
+
func Test_whichwrap()
set whichwrap=b,s
call assert_equal('b,s', &whichwrap)
@@ -1037,15 +1039,6 @@
call assert_equal(sort(copy(options)), options)
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()
" Only do this for Windows Vista and later, fails on Windows XP and earlier.
" Doesn't hurt to do this on a non-Windows system.
diff --git a/src/testdir/test_options_all.vim b/src/testdir/test_options_all.vim
new file mode 100644
index 0000000..a2330ec
--- /dev/null
+++ b/src/testdir/test_options_all.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index e979c04..cd49f58 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 797,
+/**/
796,
/**/
795,