Files
micropython/tests/net_hosted/connect_nonblock_xfer.py
Damien George 69023622ee 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>
2024-11-13 11:44:09 +11:00

116 lines
3.4 KiB
Python

# test that socket.connect() on a non-blocking socket raises EINPROGRESS
# and that an immediate write/send/read/recv does the right thing
import errno
import select
import socket
import ssl
# only mbedTLS supports non-blocking mode
if not hasattr(ssl, "MBEDTLS_VERSION"):
print("SKIP")
raise SystemExit
# 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.
# If handshake is true, the initial connect (and TLS handshake) is
# allowed to be performed before returning.
def do_connect(peer_addr, tls, handshake):
s = socket.socket()
s.setblocking(False)
try:
# print("Connecting to", peer_addr)
s.connect(peer_addr)
except OSError as er:
print("connect:", errno_name(er.errno))
# wrap with ssl/tls if desired
if tls:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
try:
s = ssl_context.wrap_socket(s, do_handshake_on_connect=handshake)
print("wrap ok: True")
except Exception as e:
print("wrap er:", e)
return s
# poll a socket and print out the result
def poll(s):
poller = select.poll()
poller.register(s)
print("poll: ", poller.poll(0))
# 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)
poll(s)
try:
ret = s.send(b"1234")
print("send ok:", ret) # shouldn't get here
except OSError as er:
print("send er:", errno_name(er.errno))
s.close()
# connect + write
# non-blocking write should return None
if hasRW:
s = do_connect(peer_addr, tls, handshake)
poll(s)
ret = s.write(b"1234")
print("write: ", ret)
s.close()
# connect + recv
# non-blocking recv should raise EAGAIN
if hasSR:
s = do_connect(peer_addr, tls, handshake)
poll(s)
try:
ret = s.recv(10)
print("recv ok:", ret) # shouldn't get here
except OSError as er:
print("recv er:", errno_name(er.errno))
s.close()
# connect + read
# non-blocking read should return None
if hasRW:
s = do_connect(peer_addr, tls, handshake)
poll(s)
ret = s.read(10)
print("read: ", ret)
s.close()
if __name__ == "__main__":
# these tests use a non-existent test IP address, this way the connect takes forever and
# we can see EAGAIN/None (https://tools.ietf.org/html/rfc5737)
print("--- Plain sockets to nowhere ---")
test(socket.getaddrinfo("192.0.2.1", 80)[0][-1], False, False)
print("--- SSL sockets to nowhere ---")
test(socket.getaddrinfo("192.0.2.1", 443)[0][-1], True, False)
print("--- Plain sockets ---")
test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, False)
print("--- SSL sockets ---")
test(socket.getaddrinfo("micropython.org", 443)[0][-1], True, True)