From a0f9cd148eaab23b2037d2f543f3b8f5a3a7ad3c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 20:13:24 +0100 Subject: [PATCH 01/10] patch 7.4.1247 Problem: The channel test doesn't run on MS-Windows. Solution: Make it work on the MS-Windows console. (Ken Takata) --- src/testdir/test_channel.py | 6 ------ src/testdir/test_channel.vim | 36 +++++++++++++++++++++++++++++++----- src/version.c | 2 ++ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py index a706243e43..2364494831 100755 --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -7,12 +7,6 @@ # Then Vim can send requests to the server: # :let response = ch_sendexpr(handle, 'hello!') # -# And you can control Vim by typing a JSON message here, e.g.: -# ["ex","echo 'hi there'"] -# -# There is no prompt, just type a line and press Enter. -# To exit cleanly type "quit". -# # See ":help channel-demo" in Vim. # # This requires Python 2.6 or later. diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 140691c398..ee7a422447 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -2,14 +2,40 @@ scriptencoding utf-8 " This requires the Python command to run the test server. -" This most likely only works on Unix. -if !has('unix') || !executable('python') +" This most likely only works on Unix and Windows console. +if has('unix') + if !executable('python') + finish + endif +elseif has('win32') && !has('gui_win32') + " Use Python Launcher for Windows (py.exe). + if !executable('py') + finish + endif +else finish endif +func s:start_server() + if has('win32') + silent !start cmd /c start "test_channel" py test_channel.py + else + silent !./test_channel.py& + endif +endfunc + +func s:kill_server() + if has('win32') + call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"') + else + call system("killall test_channel.py") + endif +endfunc + func Test_communicate() + call delete("Xportnr") " The Python program writes the port number in Xportnr. - silent !./test_channel.py& + call s:start_server() " Wait for up to 2 seconds for the port number to be there. let cnt = 20 @@ -29,7 +55,7 @@ func Test_communicate() if len(l) == 0 " Can't make the connection, give up. - call system("killall test_channel.py") + call s:kill_server() return endif let port = l[0] @@ -49,5 +75,5 @@ func Test_communicate() " make the server quit, can't check if this works, should not hang. call ch_sendexpr(handle, '!quit!', 0) - call system("killall test_channel.py") + call s:kill_server() endfunc diff --git a/src/version.c b/src/version.c index 6aa4baa932..9417f5e445 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1247, /**/ 1246, /**/ From f92591f7f9fc78d2aced99befe444cb423b26df8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 20:22:32 +0100 Subject: [PATCH 02/10] patch 7.4.1248 Problem: Can't reliably stop the channel test server. Can't start the server if the python file is not executable. Solution: Use "pkill" instead of "killall". Run the python file as an argument instead of as an executable. --- src/testdir/test_channel.py | 0 src/testdir/test_channel.vim | 7 ++++--- src/version.c | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) mode change 100755 => 100644 src/testdir/test_channel.py diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py old mode 100755 new mode 100644 diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index ee7a422447..9d4c778372 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -4,7 +4,8 @@ scriptencoding utf-8 " This requires the Python command to run the test server. " This most likely only works on Unix and Windows console. if has('unix') - if !executable('python') + " We also need the pkill command to make sure the server can be stopped. + if !executable('python') || !executable('pkill') finish endif elseif has('win32') && !has('gui_win32') @@ -20,7 +21,7 @@ func s:start_server() if has('win32') silent !start cmd /c start "test_channel" py test_channel.py else - silent !./test_channel.py& + silent !python test_channel.py& endif endfunc @@ -28,7 +29,7 @@ func s:kill_server() if has('win32') call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"') else - call system("killall test_channel.py") + call system("pkill --full test_channel.py") endif endfunc diff --git a/src/version.c b/src/version.c index 9417f5e445..80abec4a79 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1248, /**/ 1247, /**/ From fcb1e3d16832ce06da0dc38ecb7ab9aaa3ee4383 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 21:32:46 +0100 Subject: [PATCH 03/10] patch 7.4.1249 Problem: Crash when the process a channel is connected to exits. Solution: Use the file descriptor properly. Add a test. (Damien) Also add a test for eval(). --- src/channel.c | 12 ++++++-- src/testdir/test_channel.py | 23 +++++++++++++- src/testdir/test_channel.vim | 58 ++++++++++++++++++++++++++---------- src/version.c | 2 ++ 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/src/channel.c b/src/channel.c index 2f7403a854..2e5965bc7b 100644 --- a/src/channel.c +++ b/src/channel.c @@ -698,10 +698,14 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3) } else { - typval_T *tv = eval_expr(arg, NULL); + typval_T *tv; typval_T err_tv; char_u *json; + /* Don't pollute the display with errors. */ + ++emsg_skip; + tv = eval_expr(arg, NULL); + --emsg_skip; if (is_eval) { if (tv == NULL) @@ -714,7 +718,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3) channel_send(idx, json, "eval"); vim_free(json); } - free_tv(tv); + if (tv != &err_tv) + free_tv(tv); } } else if (p_verbose > 2) @@ -1119,7 +1124,8 @@ channel_read_json_block(int ch_idx, int id, typval_T **rettv) /* Wait for up to 2 seconds. * TODO: use timeout set on the channel. */ - if (channel_wait(channels[ch_idx].ch_fd, 2000) == FAIL) + if (channels[ch_idx].ch_fd < 0 + || channel_wait(channels[ch_idx].ch_fd, 2000) == FAIL) break; channel_read(ch_idx); } diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py index 2364494831..64546c0205 100644 --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -52,7 +52,6 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): decoded = [-1, ''] # Send a response if the sequence number is positive. - # Negative numbers are used for "eval" responses. if decoded[0] >= 0: if decoded[1] == 'hello!': # simply send back a string @@ -65,9 +64,27 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): print("sending: {}".format(cmd)) thesocket.sendall(cmd.encode('utf-8')) response = "ok" + elif decoded[1] == 'eval-works': + # Send an eval request. We ignore the response. + cmd = '["eval","\\"foo\\" . 123", -1]' + print("sending: {}".format(cmd)) + thesocket.sendall(cmd.encode('utf-8')) + response = "ok" + elif decoded[1] == 'eval-fails': + # Send an eval request that will fail. + cmd = '["eval","xxx", -2]' + print("sending: {}".format(cmd)) + thesocket.sendall(cmd.encode('utf-8')) + response = "ok" + elif decoded[1] == 'eval-result': + # Send back the last received eval result. + response = last_eval elif decoded[1] == '!quit!': # we're done sys.exit(0) + elif decoded[1] == '!crash!': + # Crash! + 42 / 0 else: response = "what?" @@ -75,6 +92,10 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): print("sending: {}".format(encoded)) thesocket.sendall(encoded.encode('utf-8')) + # Negative numbers are used for "eval" responses. + elif decoded[0] < 0: + last_eval = decoded + thesocket = None class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 9d4c778372..d1289903e7 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -18,25 +18,14 @@ else endif func s:start_server() + " The Python program writes the port number in Xportnr. + call delete("Xportnr") + if has('win32') silent !start cmd /c start "test_channel" py test_channel.py else silent !python test_channel.py& endif -endfunc - -func s:kill_server() - if has('win32') - call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"') - else - call system("pkill --full test_channel.py") - endif -endfunc - -func Test_communicate() - call delete("Xportnr") - " The Python program writes the port number in Xportnr. - call s:start_server() " Wait for up to 2 seconds for the port number to be there. let cnt = 20 @@ -57,10 +46,28 @@ func Test_communicate() if len(l) == 0 " Can't make the connection, give up. call s:kill_server() - return + call assert_false(1, "Can't start test_channel.py") + return -1 endif let port = l[0] + let handle = ch_open('localhost:' . port, 'json') + return handle +endfunc + +func s:kill_server() + if has('win32') + call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"') + else + call system("pkill --full test_channel.py") + endif +endfunc + +func Test_communicate() + let handle = s:start_server() + if handle < 0 + return + endif " Simple string request and reply. call assert_equal('got it', ch_sendexpr(handle, 'hello!')) @@ -73,8 +80,29 @@ func Test_communicate() call assert_equal('added1', getline(line('$') - 1)) call assert_equal('added2', getline('$')) + " Send an eval request that works. + call assert_equal('ok', ch_sendexpr(handle, 'eval-works')) + call assert_equal([-1, 'foo123'], ch_sendexpr(handle, 'eval-result')) + + " Send an eval request that fails. + call assert_equal('ok', ch_sendexpr(handle, 'eval-fails')) + call assert_equal([-2, 'ERROR'], ch_sendexpr(handle, 'eval-result')) + " make the server quit, can't check if this works, should not hang. call ch_sendexpr(handle, '!quit!', 0) call s:kill_server() endfunc + +" Test that a server crash is handled gracefully. +func Test_server_crash() + let handle = s:start_server() + if handle < 0 + return + endif + call ch_sendexpr(handle, '!crash!') + + " kill the server in case if failed to crash + sleep 10m + call s:kill_server() +endfunc diff --git a/src/version.c b/src/version.c index 80abec4a79..d06e1617bd 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1249, /**/ 1248, /**/ From 2212c4154cde0641225782cc4dd1a6483ff2ff35 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 21:45:27 +0100 Subject: [PATCH 04/10] patch 7.4.1250 Problem: Running tests in shadow directory fails. Solution: Also link testdir/*.py --- src/Makefile | 2 ++ src/version.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Makefile b/src/Makefile index 93a889831b..8985e40f42 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2012,6 +2012,7 @@ test_arglist \ test_assert \ test_backspace_opt \ test_cdo \ + test_channel \ test_cursor_func \ test_delete \ test_expand \ @@ -2552,6 +2553,7 @@ shadow: runtime pixmaps ../../testdir/Make_all.mak \ ../../testdir/*.in \ ../../testdir/*.vim \ + ../../testdir/*.py \ ../../testdir/python* \ ../../testdir/sautest \ ../../testdir/test83-tags? \ diff --git a/src/version.c b/src/version.c index d06e1617bd..4af96f156d 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1250, /**/ 1249, /**/ From bf087cead956513bcd8d40d70322875c479a1984 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 21:56:42 +0100 Subject: [PATCH 05/10] patch 7.4.1251 Problem: New test file missing from distribution. Solution: Add src/testdir/*.py. --- Filelist | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Filelist b/Filelist index b7fb61bdf7..e0eb8826d4 100644 --- a/Filelist +++ b/Filelist @@ -91,6 +91,7 @@ SRC_ALL = \ src/testdir/README.txt \ src/testdir/Make_all.mak \ src/testdir/*.in \ + src/testdir/*.py \ src/testdir/sautest/autoload/*.vim \ src/testdir/runtest.vim \ src/testdir/test[0-9]*.ok \ diff --git a/src/version.c b/src/version.c index 4af96f156d..f88b8cbd07 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1251, /**/ 1250, /**/ From e7bed627c89ed80bc4b2d96f542819029adf6e76 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 22:20:29 +0100 Subject: [PATCH 06/10] patch 7.4.1252 Problem: The channel test server may receive two messages concatenated. Solution: Split the messages. --- src/testdir/test_channel.py | 105 ++++++++++++++++++++---------------- src/version.c | 2 + 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py index 64546c0205..486ff4d7c6 100644 --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -45,56 +45,69 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): print("=== socket closed ===") break print("received: {}".format(data)) - try: - decoded = json.loads(data) - except ValueError: - print("json decoding failed") - decoded = [-1, ''] - # Send a response if the sequence number is positive. - if decoded[0] >= 0: - if decoded[1] == 'hello!': - # simply send back a string - response = "got it" - elif decoded[1] == 'make change': - # Send two ex commands at the same time, before replying to - # the request. - cmd = '["ex","call append(\\"$\\",\\"added1\\")"]' - cmd += '["ex","call append(\\"$\\",\\"added2\\")"]' - print("sending: {}".format(cmd)) - thesocket.sendall(cmd.encode('utf-8')) - response = "ok" - elif decoded[1] == 'eval-works': - # Send an eval request. We ignore the response. - cmd = '["eval","\\"foo\\" . 123", -1]' - print("sending: {}".format(cmd)) - thesocket.sendall(cmd.encode('utf-8')) - response = "ok" - elif decoded[1] == 'eval-fails': - # Send an eval request that will fail. - cmd = '["eval","xxx", -2]' - print("sending: {}".format(cmd)) - thesocket.sendall(cmd.encode('utf-8')) - response = "ok" - elif decoded[1] == 'eval-result': - # Send back the last received eval result. - response = last_eval - elif decoded[1] == '!quit!': - # we're done - sys.exit(0) - elif decoded[1] == '!crash!': - # Crash! - 42 / 0 + # We may receive two messages at once. Take the part up to the + # matching "]" (recognized by finding "]["). + while data != '': + splitidx = data.find('][') + if splitidx < 0: + todo = data + data = '' else: - response = "what?" + todo = data[:splitidx + 1] + data = data[splitidx + 1:] + print("using: {}".format(todo)) - encoded = json.dumps([decoded[0], response]) - print("sending: {}".format(encoded)) - thesocket.sendall(encoded.encode('utf-8')) + try: + decoded = json.loads(todo) + except ValueError: + print("json decoding failed") + decoded = [-1, ''] - # Negative numbers are used for "eval" responses. - elif decoded[0] < 0: - last_eval = decoded + # Send a response if the sequence number is positive. + if decoded[0] >= 0: + if decoded[1] == 'hello!': + # simply send back a string + response = "got it" + elif decoded[1] == 'make change': + # Send two ex commands at the same time, before replying to + # the request. + cmd = '["ex","call append(\\"$\\",\\"added1\\")"]' + cmd += '["ex","call append(\\"$\\",\\"added2\\")"]' + print("sending: {}".format(cmd)) + thesocket.sendall(cmd.encode('utf-8')) + response = "ok" + elif decoded[1] == 'eval-works': + # Send an eval request. We ignore the response. + cmd = '["eval","\\"foo\\" . 123", -1]' + print("sending: {}".format(cmd)) + thesocket.sendall(cmd.encode('utf-8')) + response = "ok" + elif decoded[1] == 'eval-fails': + # Send an eval request that will fail. + cmd = '["eval","xxx", -2]' + print("sending: {}".format(cmd)) + thesocket.sendall(cmd.encode('utf-8')) + response = "ok" + elif decoded[1] == 'eval-result': + # Send back the last received eval result. + response = last_eval + elif decoded[1] == '!quit!': + # we're done + sys.exit(0) + elif decoded[1] == '!crash!': + # Crash! + 42 / 0 + else: + response = "what?" + + encoded = json.dumps([decoded[0], response]) + print("sending: {}".format(encoded)) + thesocket.sendall(encoded.encode('utf-8')) + + # Negative numbers are used for "eval" responses. + elif decoded[0] < 0: + last_eval = decoded thesocket = None diff --git a/src/version.c b/src/version.c index f88b8cbd07..f4e9febae0 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1252, /**/ 1251, /**/ From 608a8919cae982cb38e38725a843df47b234dae6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 22:39:51 +0100 Subject: [PATCH 07/10] patch 7.4.1253 Problem: Python test server not displaying second of two commands. Solaris doesn't have "pkill --full". Solution: Also echo the second command. Use "pkill -f". --- src/testdir/test_channel.py | 24 +++++++++++++----------- src/testdir/test_channel.vim | 2 +- src/version.c | 2 ++ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py index 486ff4d7c6..3133c2f408 100644 --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -34,32 +34,34 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): thesocket = self.request while True: try: - data = self.request.recv(4096).decode('utf-8') + received = self.request.recv(4096).decode('utf-8') except socket.error: print("=== socket error ===") break except IOError: print("=== socket closed ===") break - if data == '': + if received == '': print("=== socket closed ===") break - print("received: {}".format(data)) + print("received: {}".format(received)) # We may receive two messages at once. Take the part up to the # matching "]" (recognized by finding "]["). - while data != '': - splitidx = data.find('][') + todo = received + while todo != '': + splitidx = todo.find('][') if splitidx < 0: - todo = data - data = '' + used = todo + todo = '' else: - todo = data[:splitidx + 1] - data = data[splitidx + 1:] - print("using: {}".format(todo)) + used = todo[:splitidx + 1] + todo = todo[splitidx + 1:] + if used != received: + print("using: {}".format(used)) try: - decoded = json.loads(todo) + decoded = json.loads(used) except ValueError: print("json decoding failed") decoded = [-1, ''] diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index d1289903e7..de64188e20 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -59,7 +59,7 @@ func s:kill_server() if has('win32') call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"') else - call system("pkill --full test_channel.py") + call system("pkill -f test_channel.py") endif endfunc diff --git a/src/version.c b/src/version.c index f4e9febae0..2d61d4f26c 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1253, /**/ 1252, /**/ From 3b05b135e3ee4cfd59983fd63461e8f7642c1713 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 23:25:07 +0100 Subject: [PATCH 08/10] patch 7.4.1254 Problem: Opening a second channel causes a crash. (Ken Takata) Solution: Don't re-allocate the array with channels. --- src/channel.c | 21 ++++++++++++--------- src/testdir/test_channel.py | 14 ++++---------- src/testdir/test_channel.vim | 24 ++++++++++++++++++++++-- src/version.c | 2 ++ 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/channel.c b/src/channel.c index 2e5965bc7b..9e15fe4551 100644 --- a/src/channel.c +++ b/src/channel.c @@ -133,22 +133,25 @@ FILE *debugfd = NULL; add_channel(void) { int idx; - channel_T *new_channels; channel_T *ch; if (channels != NULL) + { for (idx = 0; idx < channel_count; ++idx) if (channels[idx].ch_fd < 0) /* re-use a closed channel slot */ return idx; - if (channel_count == MAX_OPEN_CHANNELS) - return -1; - new_channels = (channel_T *)alloc(sizeof(channel_T) * (channel_count + 1)); - if (new_channels == NULL) - return -1; - if (channels != NULL) - mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count); - channels = new_channels; + if (channel_count == MAX_OPEN_CHANNELS) + return -1; + } + else + { + channels = (channel_T *)alloc((int)sizeof(channel_T) + * MAX_OPEN_CHANNELS); + if (channels == NULL) + return -1; + } + ch = &channels[channel_count]; (void)vim_memset(ch, 0, sizeof(channel_T)); diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py index 3133c2f408..f1d774fd11 100644 --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -24,14 +24,10 @@ except ImportError: # Python 2 import SocketServer as socketserver -thesocket = None - class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): def handle(self): print("=== socket opened ===") - global thesocket - thesocket = self.request while True: try: received = self.request.recv(4096).decode('utf-8') @@ -77,19 +73,19 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): cmd = '["ex","call append(\\"$\\",\\"added1\\")"]' cmd += '["ex","call append(\\"$\\",\\"added2\\")"]' print("sending: {}".format(cmd)) - thesocket.sendall(cmd.encode('utf-8')) + self.request.sendall(cmd.encode('utf-8')) response = "ok" elif decoded[1] == 'eval-works': # Send an eval request. We ignore the response. cmd = '["eval","\\"foo\\" . 123", -1]' print("sending: {}".format(cmd)) - thesocket.sendall(cmd.encode('utf-8')) + self.request.sendall(cmd.encode('utf-8')) response = "ok" elif decoded[1] == 'eval-fails': # Send an eval request that will fail. cmd = '["eval","xxx", -2]' print("sending: {}".format(cmd)) - thesocket.sendall(cmd.encode('utf-8')) + self.request.sendall(cmd.encode('utf-8')) response = "ok" elif decoded[1] == 'eval-result': # Send back the last received eval result. @@ -105,14 +101,12 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): encoded = json.dumps([decoded[0], response]) print("sending: {}".format(encoded)) - thesocket.sendall(encoded.encode('utf-8')) + self.request.sendall(encoded.encode('utf-8')) # Negative numbers are used for "eval" responses. elif decoded[0] < 0: last_eval = decoded - thesocket = None - class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): pass diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index de64188e20..b416520e45 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -17,6 +17,8 @@ else finish endif +let s:port = -1 + func s:start_server() " The Python program writes the port number in Xportnr. call delete("Xportnr") @@ -49,9 +51,9 @@ func s:start_server() call assert_false(1, "Can't start test_channel.py") return -1 endif - let port = l[0] + let s:port = l[0] - let handle = ch_open('localhost:' . port, 'json') + let handle = ch_open('localhost:' . s:port, 'json') return handle endfunc @@ -94,6 +96,24 @@ func Test_communicate() call s:kill_server() endfunc +" Test that we can open two channels. +func Test_two_channels() + let handle = s:start_server() + if handle < 0 + return + endif + call assert_equal('got it', ch_sendexpr(handle, 'hello!')) + + let newhandle = ch_open('localhost:' . s:port, 'json') + call assert_equal('got it', ch_sendexpr(newhandle, 'hello!')) + call assert_equal('got it', ch_sendexpr(handle, 'hello!')) + + call ch_close(handle) + call assert_equal('got it', ch_sendexpr(newhandle, 'hello!')) + + call s:kill_server() +endfunc + " Test that a server crash is handled gracefully. func Test_server_crash() let handle = s:start_server() diff --git a/src/version.c b/src/version.c index 2d61d4f26c..6a86d6a36e 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1254, /**/ 1253, /**/ From 66624ff0d9e1de2fc5eb4f95f3a3a2ed70b10138 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 3 Feb 2016 23:59:43 +0100 Subject: [PATCH 09/10] patch 7.4.1255 Problem: Crash for channel "eval" command without third argument. Solution: Check for missing argument. --- src/channel.c | 4 ++-- src/testdir/test_channel.py | 10 ++++++++-- src/testdir/test_channel.vim | 4 ++++ src/version.c | 2 ++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/channel.c b/src/channel.c index 9e15fe4551..b9a2a972fa 100644 --- a/src/channel.c +++ b/src/channel.c @@ -694,7 +694,7 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3) { int is_eval = cmd[1] == 'v'; - if (is_eval && arg3->v_type != VAR_NUMBER) + if (is_eval && (arg3 == NULL || arg3->v_type != VAR_NUMBER)) { if (p_verbose > 2) EMSG("E904: third argument for eval must be a number"); @@ -774,7 +774,7 @@ may_invoke_callback(int idx) typval_T *arg3 = NULL; char_u *cmd = typetv->vval.v_string; - /* ["cmd", arg] */ + /* ["cmd", arg] or ["cmd", arg, arg] */ if (list->lv_len == 3) arg3 = &list->lv_last->li_tv; channel_exe_cmd(idx, cmd, &argv[1], arg3); diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py index f1d774fd11..dbf9eb2c7b 100644 --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -68,8 +68,8 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): # simply send back a string response = "got it" elif decoded[1] == 'make change': - # Send two ex commands at the same time, before replying to - # the request. + # Send two ex commands at the same time, before + # replying to the request. cmd = '["ex","call append(\\"$\\",\\"added1\\")"]' cmd += '["ex","call append(\\"$\\",\\"added2\\")"]' print("sending: {}".format(cmd)) @@ -87,6 +87,12 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): print("sending: {}".format(cmd)) self.request.sendall(cmd.encode('utf-8')) response = "ok" + elif decoded[1] == 'eval-bad': + # Send an eval request missing the third argument. + cmd = '["eval","xxx"]' + print("sending: {}".format(cmd)) + self.request.sendall(cmd.encode('utf-8')) + response = "ok" elif decoded[1] == 'eval-result': # Send back the last received eval result. response = last_eval diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index b416520e45..3caf5d21d9 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -90,6 +90,10 @@ func Test_communicate() call assert_equal('ok', ch_sendexpr(handle, 'eval-fails')) call assert_equal([-2, 'ERROR'], ch_sendexpr(handle, 'eval-result')) + " Send a bad eval request. There will be no response. + call assert_equal('ok', ch_sendexpr(handle, 'eval-bad')) + call assert_equal([-2, 'ERROR'], ch_sendexpr(handle, 'eval-result')) + " make the server quit, can't check if this works, should not hang. call ch_sendexpr(handle, '!quit!', 0) diff --git a/src/version.c b/src/version.c index 6a86d6a36e..0f87c416fd 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1255, /**/ 1254, /**/ From b3e2f00f39d6edafda6e5508a926ebd244997a0f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 4 Feb 2016 00:11:37 +0100 Subject: [PATCH 10/10] patch 7.4.1256 Problem: On Mac sys.exit(0) doesn't kill the test server. Solution: Use self.server.shutdown(). (Jun Takimoto) --- src/testdir/test_channel.py | 9 +++++---- src/version.c | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py index dbf9eb2c7b..fb75938c1f 100644 --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -98,7 +98,8 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): response = last_eval elif decoded[1] == '!quit!': # we're done - sys.exit(0) + self.server.shutdown() + break elif decoded[1] == '!crash!': # Crash! 42 / 0 @@ -127,7 +128,6 @@ if __name__ == "__main__": server_thread = threading.Thread(target=server.serve_forever) # Exit the server thread when the main thread terminates - server_thread.daemon = True server_thread.start() # Write the port number in Xportnr, so that the test knows it. @@ -135,6 +135,7 @@ if __name__ == "__main__": f.write("{}".format(port)) f.close() - # Block here print("Listening on port {}".format(port)) - server.serve_forever() + + # Main thread terminates, but the server continues running + # until server.shutdown() is called. diff --git a/src/version.c b/src/version.c index 0f87c416fd..b318dc7101 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1256, /**/ 1255, /**/