patch 7.4.1246
Problem:    The channel functionality isn't tested.
Solution:   Add a test using a Python test server.
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index d7a06ee..bb92904 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -171,6 +171,7 @@
 NEW_TESTS = test_arglist.res \
 	    test_assert.res \
 	    test_cdo.res \
+	    test_channel.res \
 	    test_hardcopy.res \
 	    test_increment.res \
 	    test_langmap.res \
diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py
new file mode 100644
index 0000000..a706243
--- /dev/null
+++ b/src/testdir/test_channel.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+#
+# Server that will accept connections from a Vim channel.
+# Run this server and then in Vim you can open the channel:
+#  :let handle = ch_open('localhost:8765', 'json')
+#
+# 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<Enter>".
+#
+# See ":help channel-demo" in Vim.
+#
+# This requires Python 2.6 or later.
+
+from __future__ import print_function
+import json
+import socket
+import sys
+import threading
+
+try:
+    # Python 3
+    import socketserver
+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:
+                data = self.request.recv(4096).decode('utf-8')
+            except socket.error:
+                print("=== socket error ===")
+                break
+            except IOError:
+                print("=== socket closed ===")
+                break
+            if data == '':
+                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.
+            # Negative numbers are used for "eval" responses.
+            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] == '!quit!':
+                    # we're done
+                    sys.exit(0)
+                else:
+                    response = "what?"
+
+                encoded = json.dumps([decoded[0], response])
+                print("sending: {}".format(encoded))
+                thesocket.sendall(encoded.encode('utf-8'))
+
+        thesocket = None
+
+class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
+    pass
+
+if __name__ == "__main__":
+    HOST, PORT = "localhost", 0
+
+    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
+    ip, port = server.server_address
+
+    # Start a thread with the server -- that thread will then start one
+    # more thread for each request
+    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.
+    f = open("Xportnr", "w")
+    f.write("{}".format(port))
+    f.close()
+
+    # Block here
+    print("Listening on port {}".format(port))
+    server.serve_forever()
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim
new file mode 100644
index 0000000..140691c
--- /dev/null
+++ b/src/testdir/test_channel.vim
@@ -0,0 +1,53 @@
+" Test for channel functions.
+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')
+  finish
+endif
+
+func Test_communicate()
+  " The Python program writes the port number in Xportnr.
+  silent !./test_channel.py&
+
+  " Wait for up to 2 seconds for the port number to be there.
+  let cnt = 20
+  let l = []
+  while cnt > 0
+    try
+      let l = readfile("Xportnr")
+    catch
+    endtry
+    if len(l) >= 1
+      break
+    endif
+    sleep 100m
+    let cnt -= 1
+  endwhile
+  call delete("Xportnr")
+
+  if len(l) == 0
+    " Can't make the connection, give up.
+    call system("killall test_channel.py")
+    return
+  endif
+  let port = l[0]
+  let handle = ch_open('localhost:' . port, 'json')
+
+  " Simple string request and reply.
+  call assert_equal('got it', ch_sendexpr(handle, 'hello!'))
+
+  " Request that triggers sending two ex commands.  These will usually be
+  " handled before getting the response, but it's not guaranteed, thus wait a
+  " tiny bit for the commands to get executed.
+  call assert_equal('ok', ch_sendexpr(handle, 'make change'))
+  sleep 10m
+  call assert_equal('added1', getline(line('$') - 1))
+  call assert_equal('added2', getline('$'))
+
+  " 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")
+endfunc