Skip to content

Commit

Permalink
patch 8.0.0477: the client-server test may hang when failing
Browse files Browse the repository at this point in the history
Problem:    The client-server test may hang when failing.
Solution:   Set a timer.  Add assert_report()
  • Loading branch information
brammool committed Mar 18, 2017
1 parent 7a43cb9 commit 4220555
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 64 deletions.
43 changes: 28 additions & 15 deletions runtime/doc/eval.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*eval.txt* For Vim version 8.0. Last change: 2017 Mar 09
*eval.txt* For Vim version 8.0. Last change: 2017 Mar 18


VIM REFERENCE MANUAL by Bram Moolenaar
Expand Down Expand Up @@ -1986,16 +1986,23 @@ argidx() Number current index in the argument list
arglistid([{winnr} [, {tabnr}]]) Number argument list id
argv({nr}) String {nr} entry of the argument list
argv() List the argument list
assert_equal({exp}, {act} [, {msg}]) none assert {exp} is equal to {act}
assert_exception({error} [, {msg}]) none assert {error} is in v:exception
assert_fails({cmd} [, {error}]) none assert {cmd} fails
assert_false({actual} [, {msg}]) none assert {actual} is false
assert_equal({exp}, {act} [, {msg}])
none assert {exp} is equal to {act}
assert_exception({error} [, {msg}])
none assert {error} is in v:exception
assert_fails({cmd} [, {error}]) none assert {cmd} fails
assert_false({actual} [, {msg}])
none assert {actual} is false
assert_inrange({lower}, {upper}, {actual} [, {msg}])
none assert {actual} is inside the range
assert_match({pat}, {text} [, {msg}]) none assert {pat} matches {text}
assert_notequal({exp}, {act} [, {msg}]) none assert {exp} is not equal {act}
assert_notmatch({pat}, {text} [, {msg}]) none assert {pat} not matches {text}
assert_true({actual} [, {msg}]) none assert {actual} is true
assert_match({pat}, {text} [, {msg}])
none assert {pat} matches {text}
assert_notequal({exp}, {act} [, {msg}])
none assert {exp} is not equal {act}
assert_notmatch({pat}, {text} [, {msg}])
none assert {pat} not matches {text}
assert_report({msg}) none report a test failure
assert_true({actual} [, {msg}]) none assert {actual} is true
asin({expr}) Float arc sine of {expr}
atan({expr}) Float arc tangent of {expr}
atan2({expr1}, {expr2}) Float arc tangent of {expr1} / {expr2}
Expand Down Expand Up @@ -2583,7 +2590,10 @@ assert_notmatch({pattern}, {actual} [, {msg}])
The opposite of `assert_match()`: add an error message to
|v:errors| when {pattern} matches {actual}.

assert_true({actual} [, {msg}]) *assert_true()*
assert_report({msg}) *assert_report()*
Report a test failure directly, using {msg}.

assert_true({actual} [, {msg}]) *assert_true()*
When {actual} is not true an error message is added to
|v:errors|, like with |assert_equal()|.
A value is TRUE when it is a non-zero number. When {actual}
Expand Down Expand Up @@ -3925,11 +3935,14 @@ foldtext() Returns a String, to be displayed for a closed fold. This is
|v:foldstart|, |v:foldend| and |v:folddashes| variables.
The returned string looks like this: >
+-- 45 lines: abcdef
< The number of dashes depends on the foldlevel. The "45" is
the number of lines in the fold. "abcdef" is the text in the
first non-blank line of the fold. Leading white space, "//"
or "/*" and the text from the 'foldmarker' and 'commentstring'
options is removed.
< The number of leading dashes depends on the foldlevel. The
"45" is the number of lines in the fold. "abcdef" is the text
in the first non-blank line of the fold. Leading white space,
"//" or "/*" and the text from the 'foldmarker' and
'commentstring' options is removed.
When used to draw the actual foldtext, the rest of the line
will be filled with the fold char from the 'fillchars'
setting.
{not available when compiled without the |+folding| feature}

foldtextresult({lnum}) *foldtextresult()*
Expand Down
11 changes: 11 additions & 0 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -9083,6 +9083,17 @@ assert_bool(typval_T *argvars, int isTrue)
}
}

void
assert_report(typval_T *argvars)
{
garray_T ga;

prepare_assert_error(&ga);
ga_concat(&ga, get_tv_string(&argvars[0]));
assert_error(&ga);
ga_clear(&ga);
}

void
assert_exception(typval_T *argvars)
{
Expand Down
11 changes: 11 additions & 0 deletions src/evalfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ static void f_assert_inrange(typval_T *argvars, typval_T *rettv);
static void f_assert_match(typval_T *argvars, typval_T *rettv);
static void f_assert_notequal(typval_T *argvars, typval_T *rettv);
static void f_assert_notmatch(typval_T *argvars, typval_T *rettv);
static void f_assert_report(typval_T *argvars, typval_T *rettv);
static void f_assert_true(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_FLOAT
static void f_asin(typval_T *argvars, typval_T *rettv);
Expand Down Expand Up @@ -483,6 +484,7 @@ static struct fst
{"assert_match", 2, 3, f_assert_match},
{"assert_notequal", 2, 3, f_assert_notequal},
{"assert_notmatch", 2, 3, f_assert_notmatch},
{"assert_report", 1, 1, f_assert_report},
{"assert_true", 1, 2, f_assert_true},
#ifdef FEAT_FLOAT
{"atan", 1, 1, f_atan},
Expand Down Expand Up @@ -1313,6 +1315,15 @@ f_assert_notmatch(typval_T *argvars, typval_T *rettv UNUSED)
assert_match_common(argvars, ASSERT_NOTMATCH);
}

/*
* "assert_report(msg)" function
*/
static void
f_assert_report(typval_T *argvars, typval_T *rettv UNUSED)
{
assert_report(argvars);
}

/*
* "assert_true(actual[, msg])" function
*/
Expand Down
4 changes: 4 additions & 0 deletions src/if_xcmdsrv.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,10 @@ ServerWait(
if (seconds >= 0 && (now - start) >= seconds)
break;

#ifdef FEAT_TIMERS
check_due_timer();
#endif

/* Just look out for the answer without calling back into Vim */
if (localLoop)
{
Expand Down
3 changes: 3 additions & 0 deletions src/os_mswin.c
Original file line number Diff line number Diff line change
Expand Up @@ -2570,6 +2570,9 @@ serverGetReply(HWND server, int *expr_res, int remove, int wait)
/* Loop until we receive a reply */
while (reply_received == 0)
{
#ifdef FEAT_TIMERS
check_due_timer();
#endif
/* Wait for a SendMessage() call to us. This could be the reply
* we are waiting for. Use a timeout of a second, to catch the
* situation that the server died unexpectedly. */
Expand Down
1 change: 1 addition & 0 deletions src/proto/eval.pro
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ void assert_equal_common(typval_T *argvars, assert_type_T atype);
void assert_match_common(typval_T *argvars, assert_type_T atype);
void assert_inrange(typval_T *argvars);
void assert_bool(typval_T *argvars, int isTrue);
void assert_report(typval_T *argvars);
void assert_exception(typval_T *argvars);
void assert_fails(typval_T *argvars);
void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, typval_T *got_tv, assert_type_T atype);
Expand Down
104 changes: 58 additions & 46 deletions src/testdir/runtest.vim
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function GetAllocId(name)
return lnum - top - 1
endfunc

function RunTheTest(test)
func RunTheTest(test)
echo 'Executing ' . a:test

" Avoid stopping at the "hit enter" prompt
Expand Down Expand Up @@ -142,6 +142,60 @@ function RunTheTest(test)
set nomodified
endfunc

func AfterTheTest()
if len(v:errors) > 0
let s:fail += 1
call add(s:errors, 'Found errors in ' . s:test . ':')
call extend(s:errors, v:errors)
let v:errors = []
endif
endfunc

" This function can be called by a test if it wants to abort testing.
func FinishTesting()
call AfterTheTest()

" Don't write viminfo on exit.
set viminfo=

if s:fail == 0
" Success, create the .res file so that make knows it's done.
exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
write
endif

if len(s:errors) > 0
" Append errors to test.log
split test.log
call append(line('$'), '')
call append(line('$'), 'From ' . g:testname . ':')
call append(line('$'), s:errors)
write
endif

let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
echo message
call add(s:messages, message)
if s:fail > 0
let message = s:fail . ' FAILED:'
echo message
call add(s:messages, message)
call extend(s:messages, s:errors)
endif

" Add SKIPPED messages
call extend(s:messages, s:skipped)

" Append messages to the file "messages"
split messages
call append(line('$'), '')
call append(line('$'), 'From ' . g:testname . ':')
call append(line('$'), s:messages)
write

qall!
endfunc

" Source the test script. First grab the file name, in case the script
" navigates away. g:testname can be used by the tests.
let g:testname = expand('%')
Expand All @@ -164,6 +218,7 @@ endif

" Names of flaky tests.
let s:flaky = [
\ 'Test_client_server()',
\ 'Test_close_and_exit_cb()',
\ 'Test_collapse_buffers()',
\ 'Test_communicate()',
Expand Down Expand Up @@ -197,52 +252,9 @@ for s:test in sort(s:tests)
call RunTheTest(s:test)
endif

if len(v:errors) > 0
let s:fail += 1
call add(s:errors, 'Found errors in ' . s:test . ':')
call extend(s:errors, v:errors)
let v:errors = []
endif
call AfterTheTest()
endfor

" Don't write viminfo on exit.
set viminfo=

if s:fail == 0
" Success, create the .res file so that make knows it's done.
exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
write
endif

if len(s:errors) > 0
" Append errors to test.log
split test.log
call append(line('$'), '')
call append(line('$'), 'From ' . g:testname . ':')
call append(line('$'), s:errors)
write
endif

let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
echo message
call add(s:messages, message)
if s:fail > 0
let message = s:fail . ' FAILED:'
echo message
call add(s:messages, message)
call extend(s:messages, s:errors)
endif

" Add SKIPPED messages
call extend(s:messages, s:skipped)

" Append messages to the file "messages"
split messages
call append(line('$'), '')
call append(line('$'), 'From ' . g:testname . ':')
call append(line('$'), s:messages)
write

qall!
call FinishTesting()

" vim: shiftwidth=2 sts=2 expandtab
34 changes: 31 additions & 3 deletions src/testdir/test_clientserver.vim
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,87 @@ endif

source shared.vim

let s:where = 0
func Abort(id)
call assert_report('Test timed out at ' . s:where)
call FinishTesting()
endfunc

func Test_client_server()
let cmd = GetVimCommand()
if cmd == ''
return
endif
let name = 'XVIMTEXT'

" Some of these commands may hang when failing.
call timer_start(10000, 'Abort')

let s:where = 1
let name = 'XVIMTEST'
let cmd .= ' --servername ' . name
let g:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
call WaitFor('job_status(g:job) == "run"')
if job_status(g:job) != 'run'
call assert_true(0, 'Cannot run the Vim server')
call assert_report('Cannot run the Vim server')
return
endif
let s:where = 2

" Takes a short while for the server to be active.
call WaitFor('serverlist() =~ "' . name . '"')
call assert_match(name, serverlist())
let s:where = 3

call remote_foreground(name)
let s:where = 4

call remote_send(name, ":let testvar = 'yes'\<CR>")
let s:where = 5
call WaitFor('remote_expr("' . name . '", "testvar") == "yes"')
let s:where = 6
call assert_equal('yes', remote_expr(name, "testvar"))
let s:where = 7

if has('unix') && has('gui') && !has('gui_running')
" Running in a terminal and the GUI is avaiable: Tell the server to open
" the GUI and check that the remote command still works.
" Need to wait for the GUI to start up, otherwise the send hangs in trying
" to send to the terminal window.
call remote_send(name, ":gui -f\<CR>")
let s:where = 8
sleep 500m
call remote_send(name, ":let testvar = 'maybe'\<CR>")
let s:where = 9
call WaitFor('remote_expr("' . name . '", "testvar") == "maybe"')
let s:where = 10
call assert_equal('maybe', remote_expr(name, "testvar"))
let s:where = 11
endif

call assert_fails('call remote_send("XXX", ":let testvar = ''yes''\<CR>")', 'E241')
let s:where = 12

" Expression evaluated locally.
if v:servername == ''
call remote_startserver('MYSELF')
let s:where = 13
call assert_equal('MYSELF', v:servername)
endif
let g:testvar = 'myself'
call assert_equal('myself', remote_expr(v:servername, 'testvar'))
let s:where = 14

call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid')
let s:where = 15
call assert_equal('got it', remote_read(g:myserverid))
let s:where = 16

call remote_send(name, ":qa!\<CR>")
let s:where = 17
call WaitFor('job_status(g:job) == "dead"')
let s:where = 18
if job_status(g:job) != 'dead'
call assert_true(0, 'Server did not exit')
call assert_report('Server did not exit')
call job_stop(g:job, 'kill')
endif
endfunc
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
477,
/**/
476,
/**/
Expand Down

0 comments on commit 4220555

Please sign in to comment.