summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2024-11-05 11:19:45 +1100
committerDamien George <damien@micropython.org>2024-11-13 11:44:09 +1100
commit69023622ee837df051ef7e606c2f0511015d01f7 (patch)
tree6c7290c6d7c1d4708611b03d649c34bce40b92db
parenteab2869990b83dc6baa451f8c4983a312539a0be (diff)
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>
-rw-r--r--tests/net_hosted/connect_nonblock_xfer.py130
-rw-r--r--tests/net_hosted/connect_nonblock_xfer.py.exp44
2 files changed, 94 insertions, 80 deletions
diff --git a/tests/net_hosted/connect_nonblock_xfer.py b/tests/net_hosted/connect_nonblock_xfer.py
index dc4693cea..23620908a 100644
--- a/tests/net_hosted/connect_nonblock_xfer.py
+++ b/tests/net_hosted/connect_nonblock_xfer.py
@@ -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
+# 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=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
+def test(peer_addr, tls, handshake):
+ # MicroPython plain and TLS sockets have read/write
+ hasRW = True
- # 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)
+ # 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)
diff --git a/tests/net_hosted/connect_nonblock_xfer.py.exp b/tests/net_hosted/connect_nonblock_xfer.py.exp
new file mode 100644
index 000000000..c5498a038
--- /dev/null
+++ b/tests/net_hosted/connect_nonblock_xfer.py.exp
@@ -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