tests/net_hosted: Improve and simplify non-block-xfer test.
CPython changed its non-blocking socket behaviour recently and this test would not run under CPython anymore. So the following steps were taken to get the test working again and then simplify it: - Run the test against CPython 3.10.10 and capture the output into the .exp file for the test. - Run this test on unix port of MicroPython and verify that the output matches the CPython 3.10.10 output in the new .exp file (it did). From now on take unix MicroPython as the source of truth for this test when modifying it. - Remove all code that was there for CPython compatibility. - Make it print out more useful information during the test run, including names of the OSError errno values. - Add polling of the socket before the send/write/recv/read to verify that the poll gives the correct result in non-blocking mode. Tested on unix MicroPython, ESP32_GENERIC, PYBD_SF2 and RPI_PICO_W boards. Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
@@ -1,15 +1,24 @@
|
||||
# test that socket.connect() on a non-blocking socket raises EINPROGRESS
|
||||
# and that an immediate write/send/read/recv does the right thing
|
||||
|
||||
import sys, time, socket, errno, ssl
|
||||
import errno
|
||||
import select
|
||||
import socket
|
||||
import ssl
|
||||
|
||||
isMP = sys.implementation.name == "micropython"
|
||||
# only mbedTLS supports non-blocking mode
|
||||
if not hasattr(ssl, "MBEDTLS_VERSION"):
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
|
||||
def dp(e):
|
||||
# uncomment next line for development and testing, to print the actual exceptions
|
||||
# print(repr(e))
|
||||
pass
|
||||
# get the name of an errno error code
|
||||
def errno_name(er):
|
||||
if er == errno.EAGAIN:
|
||||
return "EAGAIN"
|
||||
if er == errno.EINPROGRESS:
|
||||
return "EINPROGRESS"
|
||||
return er
|
||||
|
||||
|
||||
# do_connect establishes the socket and wraps it if tls is True.
|
||||
@@ -22,112 +31,75 @@ def do_connect(peer_addr, tls, handshake):
|
||||
# print("Connecting to", peer_addr)
|
||||
s.connect(peer_addr)
|
||||
except OSError as er:
|
||||
print("connect:", er.errno == errno.EINPROGRESS)
|
||||
if er.errno != errno.EINPROGRESS:
|
||||
print(" got", er.errno)
|
||||
print("connect:", errno_name(er.errno))
|
||||
# wrap with ssl/tls if desired
|
||||
if tls:
|
||||
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
if hasattr(ssl_context, "check_hostname"):
|
||||
ssl_context.check_hostname = False
|
||||
|
||||
try:
|
||||
s = ssl_context.wrap_socket(s, do_handshake_on_connect=handshake)
|
||||
print("wrap: True")
|
||||
print("wrap ok: True")
|
||||
except Exception as e:
|
||||
dp(e)
|
||||
print("wrap:", e)
|
||||
elif handshake:
|
||||
# just sleep a little bit, this allows any connect() errors to happen
|
||||
time.sleep(0.2)
|
||||
print("wrap er:", e)
|
||||
return s
|
||||
|
||||
|
||||
# test runs the test against a specific peer address.
|
||||
def test(peer_addr, tls=False, handshake=False):
|
||||
# MicroPython plain sockets have read/write, but CPython's don't
|
||||
# MicroPython TLS sockets and CPython's have read/write
|
||||
# hasRW captures this wonderful state of affairs
|
||||
hasRW = isMP or tls
|
||||
# poll a socket and print out the result
|
||||
def poll(s):
|
||||
poller = select.poll()
|
||||
poller.register(s)
|
||||
print("poll: ", poller.poll(0))
|
||||
|
||||
# MicroPython plain sockets and CPython's have send/recv
|
||||
# MicroPython TLS sockets don't have send/recv, but CPython's do
|
||||
# hasSR captures this wonderful state of affairs
|
||||
hasSR = not (isMP and tls)
|
||||
|
||||
# test runs the test against a specific peer address.
|
||||
def test(peer_addr, tls, handshake):
|
||||
# MicroPython plain and TLS sockets have read/write
|
||||
hasRW = True
|
||||
|
||||
# MicroPython plain sockets have send/recv
|
||||
# MicroPython TLS sockets don't have send/recv
|
||||
hasSR = not tls
|
||||
|
||||
# connect + send
|
||||
# non-blocking send should raise EAGAIN
|
||||
if hasSR:
|
||||
s = do_connect(peer_addr, tls, handshake)
|
||||
# send -> 4 or EAGAIN
|
||||
poll(s)
|
||||
try:
|
||||
ret = s.send(b"1234")
|
||||
print("send:", handshake and ret == 4)
|
||||
print("send ok:", ret) # shouldn't get here
|
||||
except OSError as er:
|
||||
#
|
||||
dp(er)
|
||||
print("send:", er.errno in (errno.EAGAIN, errno.EINPROGRESS))
|
||||
print("send er:", errno_name(er.errno))
|
||||
s.close()
|
||||
else: # fake it...
|
||||
print("connect:", True)
|
||||
if tls:
|
||||
print("wrap:", True)
|
||||
print("send:", True)
|
||||
|
||||
# connect + write
|
||||
# non-blocking write should return None
|
||||
if hasRW:
|
||||
s = do_connect(peer_addr, tls, handshake)
|
||||
# write -> None
|
||||
try:
|
||||
ret = s.write(b"1234")
|
||||
print("write:", ret in (4, None)) # SSL may accept 4 into buffer
|
||||
except OSError as er:
|
||||
dp(er)
|
||||
print("write:", False) # should not raise
|
||||
except ValueError as er: # CPython
|
||||
dp(er)
|
||||
print("write:", er.args[0] == "Write on closed or unwrapped SSL socket.")
|
||||
poll(s)
|
||||
ret = s.write(b"1234")
|
||||
print("write: ", ret)
|
||||
s.close()
|
||||
else: # fake it...
|
||||
print("connect:", True)
|
||||
if tls:
|
||||
print("wrap:", True)
|
||||
print("write:", True)
|
||||
|
||||
# connect + recv
|
||||
# non-blocking recv should raise EAGAIN
|
||||
if hasSR:
|
||||
# connect + recv
|
||||
s = do_connect(peer_addr, tls, handshake)
|
||||
# recv -> EAGAIN
|
||||
poll(s)
|
||||
try:
|
||||
print("recv:", s.recv(10))
|
||||
ret = s.recv(10)
|
||||
print("recv ok:", ret) # shouldn't get here
|
||||
except OSError as er:
|
||||
dp(er)
|
||||
print("recv:", er.errno == errno.EAGAIN)
|
||||
print("recv er:", errno_name(er.errno))
|
||||
s.close()
|
||||
else: # fake it...
|
||||
print("connect:", True)
|
||||
if tls:
|
||||
print("wrap:", True)
|
||||
print("recv:", True)
|
||||
|
||||
# connect + read
|
||||
# non-blocking read should return None
|
||||
if hasRW:
|
||||
s = do_connect(peer_addr, tls, handshake)
|
||||
# read -> None
|
||||
try:
|
||||
ret = s.read(10)
|
||||
print("read:", ret is None)
|
||||
except OSError as er:
|
||||
dp(er)
|
||||
print("read:", False) # should not raise
|
||||
except ValueError as er: # CPython
|
||||
dp(er)
|
||||
print("read:", er.args[0] == "Read on closed or unwrapped SSL socket.")
|
||||
poll(s)
|
||||
ret = s.read(10)
|
||||
print("read: ", ret)
|
||||
s.close()
|
||||
else: # fake it...
|
||||
print("connect:", True)
|
||||
if tls:
|
||||
print("wrap:", True)
|
||||
print("read:", True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -136,10 +108,8 @@ if __name__ == "__main__":
|
||||
print("--- Plain sockets to nowhere ---")
|
||||
test(socket.getaddrinfo("192.0.2.1", 80)[0][-1], False, False)
|
||||
print("--- SSL sockets to nowhere ---")
|
||||
# this test fails with AXTLS because do_handshake=False blocks on first read/write and
|
||||
# there it times out until the connect is aborted
|
||||
test(socket.getaddrinfo("192.0.2.1", 443)[0][-1], True, False)
|
||||
print("--- Plain sockets ---")
|
||||
test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, True)
|
||||
test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, False)
|
||||
print("--- SSL sockets ---")
|
||||
test(socket.getaddrinfo("micropython.org", 443)[0][-1], True, True)
|
||||
|
||||
44
tests/net_hosted/connect_nonblock_xfer.py.exp
Normal file
44
tests/net_hosted/connect_nonblock_xfer.py.exp
Normal file
@@ -0,0 +1,44 @@
|
||||
--- Plain sockets to nowhere ---
|
||||
connect: EINPROGRESS
|
||||
poll: []
|
||||
send er: EAGAIN
|
||||
connect: EINPROGRESS
|
||||
poll: []
|
||||
write: None
|
||||
connect: EINPROGRESS
|
||||
poll: []
|
||||
recv er: EAGAIN
|
||||
connect: EINPROGRESS
|
||||
poll: []
|
||||
read: None
|
||||
--- SSL sockets to nowhere ---
|
||||
connect: EINPROGRESS
|
||||
wrap ok: True
|
||||
poll: []
|
||||
write: None
|
||||
connect: EINPROGRESS
|
||||
wrap ok: True
|
||||
poll: []
|
||||
read: None
|
||||
--- Plain sockets ---
|
||||
connect: EINPROGRESS
|
||||
poll: []
|
||||
send er: EAGAIN
|
||||
connect: EINPROGRESS
|
||||
poll: []
|
||||
write: None
|
||||
connect: EINPROGRESS
|
||||
poll: []
|
||||
recv er: EAGAIN
|
||||
connect: EINPROGRESS
|
||||
poll: []
|
||||
read: None
|
||||
--- SSL sockets ---
|
||||
connect: EINPROGRESS
|
||||
wrap ok: True
|
||||
poll: [(<SSLSocket>, 4)]
|
||||
write: 4
|
||||
connect: EINPROGRESS
|
||||
wrap ok: True
|
||||
poll: [(<SSLSocket>, 4)]
|
||||
read: None
|
||||
Reference in New Issue
Block a user