From 0fbdd572d43b0eb69c12a061b8080a94c58c26bb Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Jul 2025 20:09:31 +1000 Subject: [PATCH 01/34] tests/extmod/machine_spi_rate.py: Support samd with default SPI. Signed-off-by: Damien George --- tests/extmod/machine_spi_rate.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py index fe15b66fe648a..856b614a8b606 100644 --- a/tests/extmod/machine_spi_rate.py +++ b/tests/extmod/machine_spi_rate.py @@ -16,6 +16,9 @@ if "alif" in sys.platform: MAX_DELTA_MS = 20 spi_instances = ((0, None, None, None),) +elif "samd" in sys.platform: + # Use default SPI instance (tested working on ADAFRUIT_ITSYBITSY_M0_EXPRESS). + spi_instances = ((None, None, None, None),) elif "pyboard" in sys.platform: spi_instances = ( (1, None, None, None), # "explicit choice of sck/mosi/miso is not implemented" @@ -111,8 +114,10 @@ def test_spi(spi_id, sck, mosi, miso, baudrate, polarity, phase, print_results): polarity=polarity, phase=phase, ) - else: + elif spi_id is not None: s = SPI(spi_id, baudrate=baudrate, polarity=polarity, phase=phase) + else: + s = SPI(baudrate=baudrate, polarity=polarity, phase=phase) # to keep the test runtime down, use shorter buffer for lower baud rate wr_buf = wr_long if baudrate > 500_000 else wr_short From f8efe44258d3b3dff8af158cf68c93159c8444ab Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Jul 2025 20:09:54 +1000 Subject: [PATCH 02/34] tests/extmod: Support ESP32-C3 targets. Some boards define ESP32C3, some define ESP32-C3. Probably it should just use default pins, eg SPI(1). Signed-off-by: Damien George --- tests/extmod/machine_i2s_rate.py | 5 ++++- tests/extmod/machine_spi_rate.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/extmod/machine_i2s_rate.py b/tests/extmod/machine_i2s_rate.py index c8fa11514c96b..a38874fa06523 100644 --- a/tests/extmod/machine_i2s_rate.py +++ b/tests/extmod/machine_i2s_rate.py @@ -26,7 +26,10 @@ (2, Pin("D4"), Pin("D3"), Pin("D2"), None), ) elif "esp32" in sys.platform: - i2s_instances = ((0, Pin(18), Pin(19), Pin(21), Pin(14)),) + if "ESP32C3" in sys.implementation._machine or "ESP32-C3" in sys.implementation._machine: + i2s_instances = ((0, Pin(5), Pin(6), Pin(7), Pin(4)),) + else: + i2s_instances = ((0, Pin(18), Pin(19), Pin(21), Pin(14)),) # Allow for small additional RTOS overhead MAX_DELTA_MS = 8 diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py index 856b614a8b606..82080d1955850 100644 --- a/tests/extmod/machine_spi_rate.py +++ b/tests/extmod/machine_spi_rate.py @@ -28,7 +28,7 @@ spi_instances = ((0, Pin(18), Pin(19), Pin(16)),) elif "esp32" in sys.platform: impl = str(sys.implementation) - if any(soc in impl for soc in ("ESP32C2", "ESP32C3", "ESP32C6")): + if any(soc in impl for soc in ("ESP32C2", "ESP32C3", "ESP32-C3", "ESP32C6")): spi_instances = ((1, Pin(4), Pin(5), Pin(6)),) else: spi_instances = ((1, Pin(18), Pin(19), Pin(21)), (2, Pin(18), Pin(19), Pin(21))) From 2e3e758e2ca751c39ef75f1df89fd45e0a1b647c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Jul 2025 20:12:04 +1000 Subject: [PATCH 03/34] tests/extmod/machine_spi_rate.py: Use default pins on ESP32. Signed-off-by: Damien George --- tests/extmod/machine_spi_rate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py index 82080d1955850..f78a089aae7e9 100644 --- a/tests/extmod/machine_spi_rate.py +++ b/tests/extmod/machine_spi_rate.py @@ -29,9 +29,9 @@ elif "esp32" in sys.platform: impl = str(sys.implementation) if any(soc in impl for soc in ("ESP32C2", "ESP32C3", "ESP32-C3", "ESP32C6")): - spi_instances = ((1, Pin(4), Pin(5), Pin(6)),) + spi_instances = ((1, None, None, None),) else: - spi_instances = ((1, Pin(18), Pin(19), Pin(21)), (2, Pin(18), Pin(19), Pin(21))) + spi_instances = ((1, None, None, None), (2, None, None, None)) elif "esp8266" in sys.platform: MAX_DELTA_MS = 50 # port requires much looser timing requirements spi_instances = ((1, None, None, None),) # explicit pin choice not allowed From 99a9155ebbf09ae4a4f38185a0e3ede7318360ef Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 11:56:29 +1000 Subject: [PATCH 04/34] tests/run-perfbench.py: Skip bm_hexiom on small targets. Signed-off-by: Damien George --- tests/run-perfbench.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index 039d11a36111e..b5394b98f654b 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -97,14 +97,20 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): print(test_file + ": ", end="") # Check if test should be skipped + skip_reason = "" skip = ( skip_complex and test_file.find("bm_fft") != -1 or skip_native and test_file.find("viper_") != -1 ) + # bm_hexiom is large. Assume a target without complex support + # doesn't have much RAM and also so skip bm_hexiom. + if skip_complex and test_file.find("bm_hexiom") != -1: + skip = True + skip_reason = "too large" if skip: - test_results.append((test_file, "skip", "")) + test_results.append((test_file, "skip", skip_reason)) print("SKIP") continue From 2c1fe8e1b63e93c25ef0b4509beba4aec153d0de Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 12:24:51 +1000 Subject: [PATCH 05/34] tests/net_inet: Skip tests on axTLS when necessary. Signed-off-by: Damien George --- tests/net_inet/asyncio_tls_open_connection_readline.py | 4 ++++ tests/net_inet/ssl_cert.py | 3 +++ tests/net_inet/ssl_errors.py | 4 ++++ tests/net_inet/test_sslcontext_client.py | 4 ++++ tests/net_inet/test_tls_nonblock.py | 7 +++++++ 5 files changed, 22 insertions(+) diff --git a/tests/net_inet/asyncio_tls_open_connection_readline.py b/tests/net_inet/asyncio_tls_open_connection_readline.py index a0df88be4af1b..492b85a5dbd24 100644 --- a/tests/net_inet/asyncio_tls_open_connection_readline.py +++ b/tests/net_inet/asyncio_tls_open_connection_readline.py @@ -2,6 +2,10 @@ import os import asyncio +if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + # This certificate was obtained from micropython.org using openssl: # $ openssl s_client -showcerts -connect micropython.org:443 /dev/null # The certificate is from Let's Encrypt: diff --git a/tests/net_inet/ssl_cert.py b/tests/net_inet/ssl_cert.py index 7bb270d5fda7a..4f065c5657ee2 100644 --- a/tests/net_inet/ssl_cert.py +++ b/tests/net_inet/ssl_cert.py @@ -1,6 +1,9 @@ import socket import ssl +if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit # This certificate was obtained from micropython.org using openssl: # $ openssl s_client -showcerts -connect micropython.org:443 /dev/null diff --git a/tests/net_inet/ssl_errors.py b/tests/net_inet/ssl_errors.py index bc4e5910bccd8..c23f736b09b08 100644 --- a/tests/net_inet/ssl_errors.py +++ b/tests/net_inet/ssl_errors.py @@ -3,6 +3,10 @@ import sys, errno, select, socket, ssl +if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + def test(addr, hostname, block=True): print("---", hostname) diff --git a/tests/net_inet/test_sslcontext_client.py b/tests/net_inet/test_sslcontext_client.py index 77f68da496a77..bc06dc3c5832a 100644 --- a/tests/net_inet/test_sslcontext_client.py +++ b/tests/net_inet/test_sslcontext_client.py @@ -2,6 +2,10 @@ import socket import ssl +if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + # This certificate was obtained from micropython.org using openssl: # $ openssl s_client -showcerts -connect micropython.org:443 /dev/null # The certificate is from Let's Encrypt: diff --git a/tests/net_inet/test_tls_nonblock.py b/tests/net_inet/test_tls_nonblock.py index 60af858b1f146..3f9a61413f9cf 100644 --- a/tests/net_inet/test_tls_nonblock.py +++ b/tests/net_inet/test_tls_nonblock.py @@ -1,5 +1,12 @@ import socket, ssl, errno, sys, time, select +# Although this test doesn't need ssl.CERT_REQUIRED, it does require the ssl module +# to support modern ciphers. So exclude the test on axTLS which doesn't have +# CERT_REQUIRED. +if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + def test_one(site, opts): ai = socket.getaddrinfo(site, 443, socket.AF_INET) From a2476c1b768ac9c1c1fb745bf5b25f908342e2b2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 13:34:02 +1000 Subject: [PATCH 06/34] tests/perf_bench: Skip tests when vfs doesn't exist. Eg for ESP8266-512K. Signed-off-by: Damien George --- tests/perf_bench/core_import_mpy_multi.py | 9 +++++---- tests/perf_bench/core_import_mpy_single.py | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/perf_bench/core_import_mpy_multi.py b/tests/perf_bench/core_import_mpy_multi.py index 8affa157fa0c5..3d5f89c8f68f0 100644 --- a/tests/perf_bench/core_import_mpy_multi.py +++ b/tests/perf_bench/core_import_mpy_multi.py @@ -1,8 +1,9 @@ # Test performance of importing an .mpy file many times. -import sys, io, vfs - -if not hasattr(io, "IOBase"): +try: + import sys, vfs + from io import IOBase +except ImportError: print("SKIP") raise SystemExit @@ -26,7 +27,7 @@ def f(): file_data = b'M\x06\x00\x1f\x14\x03\x0etest.py\x00\x0f\x02A\x00\x02f\x00\x0cresult\x00/-5#\x82I\x81{\x81w\x82/\x81\x05\x81\x17Iom\x82\x13\x06arg\x00\x05\x1cthis will be a string object\x00\x06\x1bthis will be a bytes object\x00\n\x07\x05\x0bconst tuple\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x81\\\x10\n\x01\x89\x07d`T2\x00\x10\x024\x02\x16\x022\x01\x16\x03"\x80{\x16\x04Qc\x02\x81d\x00\x08\x02(DD\x11\x05\x16\x06\x10\x02\x16\x072\x00\x16\x082\x01\x16\t2\x02\x16\nQc\x03`\x1a\x08\x08\x12\x13@\xb1\xb0\x18\x13Qc@\t\x08\t\x12` Qc@\t\x08\n\x12``Qc\x82@ \x0e\x03\x80\x08+)##\x12\x0b\x12\x0c\x12\r\x12\x0e*\x04Y\x12\x0f\x12\x10\x12\x11*\x03Y#\x00\xc0#\x01\xc0#\x02\xc0Qc' -class File(io.IOBase): +class File(IOBase): def __init__(self): self.off = 0 diff --git a/tests/perf_bench/core_import_mpy_single.py b/tests/perf_bench/core_import_mpy_single.py index 4d9aa67bf2f0e..843bdab934d08 100644 --- a/tests/perf_bench/core_import_mpy_single.py +++ b/tests/perf_bench/core_import_mpy_single.py @@ -2,9 +2,10 @@ # The first import of a module will intern strings that don't already exist, and # this test should be representative of what happens in a real application. -import sys, io, vfs - -if not hasattr(io, "IOBase"): +try: + import sys, vfs + from io import IOBase +except ImportError: print("SKIP") raise SystemExit @@ -81,7 +82,7 @@ def f1(): file_data = b"M\x06\x00\x1f\x81=\x1e\x0etest.py\x00\x0f\x04A0\x00\x04A1\x00\x04f0\x00\x04f1\x00\x0cresult\x00/-5\x04a0\x00\x04a1\x00\x04a2\x00\x04a3\x00\x13\x15\x17\x19\x1b\x1d\x1f!#%')+1379;=?ACEGIKMOQSUWY[]_acegikmoqsuwy{}\x7f\x81\x01\x81\x03\x81\x05\x81\x07\x81\t\x81\x0b\x81\r\x81\x0f\x81\x11\x81\x13\x81\x15\x81\x17\x81\x19\x81\x1b\x81\x1d\x81\x1f\x81!\x81#\x81%\x81'\x81)\x81+\x81-\x81/\x811\x813\x815\x817\x819\x81;\x81=\x81?\x81A\x81C\x81E\x81G\x81I\x81K\x81M\x81O\x81Q\x81S\x81U\x81W\x81Y\x81[\x81]\x81_\x81a\x81c\x81e\x81g\x81i\x81k\x81m\x81o\x81q\x81s\x81u\x81w\x81y\x81{\x81}\x81\x7f\x82\x01\x82\x03\x82\x05\x82\x07\x82\t\x82\x0b\x82\r\x82\x0f\x82\x11\x82\x13\x82\x15\x82\x17\x82\x19\x82\x1b\x82\x1d\x82\x1f\x82!\x82#\x82%\x82'\x82)\x82+\x82-\x82/\x821\x823\x825\x827\x829\x82;\x82=\x82?\x82A\x82E\x82G\x82I\x82K\nname0\x00\nname1\x00\nname2\x00\nname3\x00\nname4\x00\nname5\x00\nname6\x00\nname7\x00\nname8\x00\nname9\x00$quite_a_long_name0\x00$quite_a_long_name1\x00$quite_a_long_name2\x00$quite_a_long_name3\x00$quite_a_long_name4\x00$quite_a_long_name5\x00$quite_a_long_name6\x00$quite_a_long_name7\x00$quite_a_long_name8\x00$quite_a_long_name9\x00&quite_a_long_name10\x00&quite_a_long_name11\x00\x05\x1ethis will be a string object 0\x00\x05\x1ethis will be a string object 1\x00\x05\x1ethis will be a string object 2\x00\x05\x1ethis will be a string object 3\x00\x05\x1ethis will be a string object 4\x00\x05\x1ethis will be a string object 5\x00\x05\x1ethis will be a string object 6\x00\x05\x1ethis will be a string object 7\x00\x05\x1ethis will be a string object 8\x00\x05\x1ethis will be a string object 9\x00\x06\x1dthis will be a bytes object 0\x00\x06\x1dthis will be a bytes object 1\x00\x06\x1dthis will be a bytes object 2\x00\x06\x1dthis will be a bytes object 3\x00\x06\x1dthis will be a bytes object 4\x00\x06\x1dthis will be a bytes object 5\x00\x06\x1dthis will be a bytes object 6\x00\x06\x1dthis will be a bytes object 7\x00\x06\x1dthis will be a bytes object 8\x00\x06\x1dthis will be a bytes object 9\x00\n\x07\x05\rconst tuple 0\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 1\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 2\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 3\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 4\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 5\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 6\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 7\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 8\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 9\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x82d\x10\x12\x01i@i@\x84\x18\x84\x1fT2\x00\x10\x024\x02\x16\x02T2\x01\x10\x034\x02\x16\x032\x02\x16\x042\x03\x16\x05\"\x80{\x16\x06Qc\x04\x82\x0c\x00\n\x02($$$\x11\x07\x16\x08\x10\x02\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04@\t\x08\n\x81\x0b Qc@\t\x08\x0b\x81\x0b@Qc@\t\x08\x0c\x81\x0b`QcH\t\n\r\x81\x0b` Qc\x82\x14\x00\x0c\x03h`$$$\x11\x07\x16\x08\x10\x03\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04H\t\n\n\x81\x0b``QcH\t\n\x0b\x81\x0b\x80\x07QcH\t\n\x0c\x81\x0b\x80\x08QcH\t\n\r\x81\x0b\x80\tQc\xa08P:\x04\x80\x0b13///---997799<\x1f%\x1f\"\x1f%)\x1f\"//\x12\x0e\x12\x0f\x12\x10\x12\x11\x12\x12\x12\x13\x12\x14*\x07Y\x12\x15\x12\x16\x12\x17\x12\x18\x12\x19\x12\x1a\x12\x08\x12\x07*\x08Y\x12\x1b\x12\x1c\x12\t\x12\x1d\x12\x1e\x12\x1f*\x06Y\x12 \x12!\x12\"\x12#\x12$\x12%*\x06Y\x12&\x12'\x12(\x12)\x12*\x12+*\x06Y\x12,\x12-\x12.\x12/\x120*\x05Y\x121\x122\x123\x124\x125*\x05Y\x126\x127\x128\x129\x12:*\x05Y\x12;\x12<\x12=\x12>\x12?\x12@\x12A\x12B\x12C\x12D\x12E*\x0bY\x12F\x12G\x12H\x12I\x12J\x12K\x12L\x12M\x12N\x12O\x12P*\x0bY\x12Q\x12R\x12S\x12T\x12U\x12V\x12W\x12X\x12Y\x12Z*\nY\x12[\x12\\\x12]\x12^\x12_\x12`\x12a\x12b\x12c\x12d*\nY\x12e\x12f\x12g\x12h\x12i\x12j\x12k\x12l\x12m\x12n\x12o*\x0bY\x12p\x12q\x12r\x12s\x12t\x12u\x12v\x12w\x12x\x12y\x12z*\x0bY\x12{\x12|\x12}\x12~\x12\x7f\x12\x81\x00\x12\x81\x01\x12\x81\x02\x12\x81\x03\x12\x81\x04*\nY\x12\x81\x05\x12\x81\x06\x12\x81\x07\x12\x81\x08\x12\x81\t\x12\x81\n\x12\x81\x0b\x12\x81\x0c\x12\x81\r\x12\x81\x0e\x12\x81\x0f*\x0bY\x12\x81\x10\x12\x81\x11\x12\x81\x12\x12\x81\x13\x12\x81\x14\x12\x81\x15\x12\x81\x16\x12\x81\x17\x12\x81\x18\x12\x81\x19*\nY\x12\x81\x1a\x12\x81\x1b\x12\x81\x1c\x12\x81\x1d\x12\x81\x1e\x12\x81\x1f\x12\x81 \x12\x81!\x12\x81\"\x12\x81#\x12\x81$*\x0bY\x12\x81%\x12\x81&*\x02Y\x12\x81'\x12\x81(\x12\x81)\x12\x81*\x12\x81+\x12\x81,\x12\x81-\x12\x81.\x12\x81/\x12\x810*\nY\x12\x811\x12\x812\x12\x813\x12\x814*\x04Y\x12\x815\x12\x816\x12\x817\x12\x818*\x04Y\x12\x819\x12\x81:\x12\x81;\x12\x81<*\x04YQc\x87p\x08@\x05\x80###############################\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14\xc0#\x15\xc0#\x16\xc0#\x17\xc0#\x18\xc0#\x19\xc0#\x1a\xc0#\x1b\xc0#\x1c\xc0#\x1d\xc0Qc" -class File(io.IOBase): +class File(IOBase): def __init__(self): self.off = 0 From f78b65c79d98514bdf311e367877dd045008730d Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 13:38:00 +1000 Subject: [PATCH 07/34] tests/float/string_format_modulo2_intbig.py: Get working on esp8266. In --via-mpy --emit native mode. Signed-off-by: Damien George --- tests/float/string_format_modulo2_intbig.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/float/string_format_modulo2_intbig.py b/tests/float/string_format_modulo2_intbig.py index 8110bc7f62e2f..0157c7f44d170 100644 --- a/tests/float/string_format_modulo2_intbig.py +++ b/tests/float/string_format_modulo2_intbig.py @@ -1,5 +1,13 @@ # test formatting floats with large precision, that it doesn't overflow the buffer +try: + import micropython +except: + + class micropython: + def bytecode(f): + return f + def test(num, num_str): if num == float("inf") or num == 0.0 and num_str != "0.0": @@ -18,6 +26,12 @@ def test(num, num_str): # check most powers of 10, making sure to include exponents with 3 digits -for e in range(-101, 102): - num = pow(10, e) - test(num, "1e%d" % e) +# force bytecode emitter so it can feed the WDT on esp8266 +@micropython.bytecode +def main(): + for e in range(-101, 102): + num = pow(10, e) + test(num, "1e%d" % e) + + +main() From c5d0dd2a8c11d399d06d8449b481d90438123217 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 13:44:26 +1000 Subject: [PATCH 08/34] tests/extmod/vfs_blockdev_invalid.py: Skip if no RAM to mkfs. On ESP8266 this test can fail with --via-mpy. Signed-off-by: Damien George --- tests/extmod/vfs_blockdev_invalid.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/extmod/vfs_blockdev_invalid.py b/tests/extmod/vfs_blockdev_invalid.py index 4d00f4b00273a..78de383784315 100644 --- a/tests/extmod/vfs_blockdev_invalid.py +++ b/tests/extmod/vfs_blockdev_invalid.py @@ -45,6 +45,8 @@ def ioctl(self, op, arg): try: bdev = RAMBlockDevice(50) + vfs.VfsLfs2.mkfs(bdev) + vfs.VfsFat.mkfs(bdev) except MemoryError: print("SKIP") raise SystemExit From 2c7f8a55ee31044836f406805c16fdbcd018379e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 13:49:16 +1000 Subject: [PATCH 09/34] tests/net_hosted/ssl_verify_callback.py: Skip if necessary. Signed-off-by: Damien George --- tests/net_hosted/ssl_verify_callback.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/net_hosted/ssl_verify_callback.py b/tests/net_hosted/ssl_verify_callback.py index 0dba4e4fddcf6..cd03ff49d77f1 100644 --- a/tests/net_hosted/ssl_verify_callback.py +++ b/tests/net_hosted/ssl_verify_callback.py @@ -4,6 +4,12 @@ import socket import tls +context = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT) + +if not hasattr(context, "verify_callback"): + print("SKIP") + raise SystemExit + def verify_callback(cert, depth): print("verify_callback:", type(cert), len(cert) > 100, depth) @@ -16,7 +22,6 @@ def verify_callback_fail(cert, depth): def test(peer_addr): - context = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT) context.verify_mode = tls.CERT_OPTIONAL context.verify_callback = verify_callback s = socket.socket() From 8158596fe95bf0b2e9e923ad1a2b37d208d9a023 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 22:49:34 +1000 Subject: [PATCH 10/34] py/persistentcode: Only select hard-fp arch if compiler uses hard ABI. Because RP2350 uses float-abi=softfp! Maybe we should change that somehow? Could also use `defined(__SOFTFP__)` to detect soft (all software fp) or softfp (hardware for calculations but soft/integer for passing arguments). Signed-off-by: Damien George --- py/persistentcode.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/persistentcode.h b/py/persistentcode.h index a45d5fd182e5e..15a383c646500 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -54,9 +54,9 @@ #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X64) #elif MICROPY_EMIT_THUMB #if defined(__thumb2__) - #if defined(__ARM_FP) && (__ARM_FP & 8) == 8 + #if defined(__ARM_FP) && (__ARM_FP & 8) == 8 && defined(__ARM_PCS_VFP) #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMDP) - #elif defined(__ARM_FP) && (__ARM_FP & 4) == 4 + #elif defined(__ARM_FP) && (__ARM_FP & 4) == 4 && defined(__ARM_PCS_VFP) #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMSP) #else #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EM) From 14329f45484a3cbefe2aac4618c06b184b022846 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 22:51:04 +1000 Subject: [PATCH 11/34] tests/run-perfbench.py: Skip tests that have a MemoryError. Signed-off-by: Damien George --- tests/run-perfbench.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index b5394b98f654b..767700da0d54c 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -171,6 +171,9 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): if error is not None: if error.startswith("SKIP"): test_results.append((test_file, "skip", error)) + elif error.startswith("CRASH:") and error.find("MemoryError:") != -1: + test_results.append((test_file, "skip", "too large")) + error = "SKIP: too large" else: test_results.append((test_file, "fail", error)) print(error) From 2f4f18f15882d55dfaa02062c4d1d401e5c4040a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Jul 2025 23:10:25 +1000 Subject: [PATCH 12/34] tests/run-tests.py: Skip various tests on small SAMD targets. Tested on ADAFRUIT_ITSYBITSY_M0_EXPRESS. Signed-off-by: Damien George --- tests/run-tests.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index d6eb9a3fbc258..3a86bc324d547 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -177,6 +177,18 @@ def open(self, path, mode): "thread/thread_lock3.py", "thread/thread_shared2.py", ), + "samd/armv6m": ( + # Fails timing bounds. + "extmod/time_res.py", + # Require more detailed error messages. + "micropython/emg_exc.py", + "micropython/heapalloc_exc_compressed.py", + "micropython/heapalloc_exc_compressed_emg_exc.py", + "micropython/native_with.py", + "micropython/opt_level_lineno.py", + "micropython/viper_with.py", + "misc/print_exception.py", + ), "webassembly": ( "basics/string_format_modulo.py", # can't print nulls to stdout "basics/string_strip.py", # can't print nulls to stdout @@ -930,6 +942,8 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Skip platform-specific tests. skip_tests.update(platform_tests_to_skip.get(args.platform, ())) + if args.arch is not None: + skip_tests.update(platform_tests_to_skip.get(args.platform + "/" + args.arch, ())) # Some tests are known to fail on 64-bit machines if pyb is None and platform.architecture()[0] == "64bit": From 697c68b48b0654e19ae0cbc0039ecc964cedbfcd Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 5 Aug 2025 00:02:29 +1000 Subject: [PATCH 13/34] tests/multi_net: Skip tests on axTLS when needed. Signed-off-by: Damien George --- tests/multi_net/asyncio_tls_server_client.py | 3 +++ .../asyncio_tls_server_client_cert_required_error.py | 4 ++++ tests/multi_net/asyncio_tls_server_client_readline.py | 3 +++ tests/multi_net/asyncio_tls_server_client_verify_error.py | 3 +++ tests/multi_net/ssl_cert_ec.py | 3 +++ tests/multi_net/ssl_cert_rsa.py | 3 +++ tests/multi_net/sslcontext_check_hostname_error.py | 3 +++ tests/multi_net/sslcontext_getpeercert.py | 3 +++ tests/multi_net/sslcontext_server_client.py | 4 ++++ tests/multi_net/sslcontext_server_client_ciphers.py | 4 ++++ tests/multi_net/sslcontext_server_client_files.py | 4 ++++ tests/multi_net/sslcontext_verify_callback.py | 4 ++++ tests/multi_net/sslcontext_verify_error.py | 4 ++++ tests/multi_net/sslcontext_verify_time_error.py | 4 ++++ tests/multi_net/tls_dtls_server_client.py | 4 ++++ 15 files changed, 53 insertions(+) diff --git a/tests/multi_net/asyncio_tls_server_client.py b/tests/multi_net/asyncio_tls_server_client.py index 016f57970c092..335d50bcd0807 100644 --- a/tests/multi_net/asyncio_tls_server_client.py +++ b/tests/multi_net/asyncio_tls_server_client.py @@ -62,5 +62,8 @@ def instance0(): def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit multitest.next() asyncio.run(tcp_client(b"client data")) diff --git a/tests/multi_net/asyncio_tls_server_client_cert_required_error.py b/tests/multi_net/asyncio_tls_server_client_cert_required_error.py index eac9a98beae6c..5d66d9c1bd95d 100644 --- a/tests/multi_net/asyncio_tls_server_client_cert_required_error.py +++ b/tests/multi_net/asyncio_tls_server_client_cert_required_error.py @@ -8,6 +8,10 @@ print("SKIP") raise SystemExit +if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + PORT = 8000 # These are test certificates. See tests/README.md for details. diff --git a/tests/multi_net/asyncio_tls_server_client_readline.py b/tests/multi_net/asyncio_tls_server_client_readline.py index 6093628ce5b7b..44797de95ad3a 100644 --- a/tests/multi_net/asyncio_tls_server_client_readline.py +++ b/tests/multi_net/asyncio_tls_server_client_readline.py @@ -66,5 +66,8 @@ def instance0(): def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit multitest.next() asyncio.run(tcp_client(b"client data\nclient data2\n")) diff --git a/tests/multi_net/asyncio_tls_server_client_verify_error.py b/tests/multi_net/asyncio_tls_server_client_verify_error.py index 6dbff0482c8ae..1ce17585341c9 100644 --- a/tests/multi_net/asyncio_tls_server_client_verify_error.py +++ b/tests/multi_net/asyncio_tls_server_client_verify_error.py @@ -66,5 +66,8 @@ def instance0(): def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit multitest.next() asyncio.run(tcp_client(b"client data")) diff --git a/tests/multi_net/ssl_cert_ec.py b/tests/multi_net/ssl_cert_ec.py index 8ecbd4d34f6e8..8b487ca367ace 100644 --- a/tests/multi_net/ssl_cert_ec.py +++ b/tests/multi_net/ssl_cert_ec.py @@ -38,6 +38,9 @@ def instance0(): # Client def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) diff --git a/tests/multi_net/ssl_cert_rsa.py b/tests/multi_net/ssl_cert_rsa.py index b99493b0ae5d8..7d3cc08808fb6 100644 --- a/tests/multi_net/ssl_cert_rsa.py +++ b/tests/multi_net/ssl_cert_rsa.py @@ -38,6 +38,9 @@ def instance0(): # Client def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) diff --git a/tests/multi_net/sslcontext_check_hostname_error.py b/tests/multi_net/sslcontext_check_hostname_error.py index 6bd911ddfd7cc..34beba8b66211 100644 --- a/tests/multi_net/sslcontext_check_hostname_error.py +++ b/tests/multi_net/sslcontext_check_hostname_error.py @@ -36,6 +36,9 @@ def instance0(): # Client def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) diff --git a/tests/multi_net/sslcontext_getpeercert.py b/tests/multi_net/sslcontext_getpeercert.py index e2d2a4251163b..82c3b7bafd4c7 100644 --- a/tests/multi_net/sslcontext_getpeercert.py +++ b/tests/multi_net/sslcontext_getpeercert.py @@ -18,6 +18,9 @@ # Server def instance0(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit multitest.globals(IP=multitest.get_network_ip()) s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) diff --git a/tests/multi_net/sslcontext_server_client.py b/tests/multi_net/sslcontext_server_client.py index fd330cee78a9c..ed18d8c0c2122 100644 --- a/tests/multi_net/sslcontext_server_client.py +++ b/tests/multi_net/sslcontext_server_client.py @@ -40,6 +40,10 @@ def instance0(): # Client def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) diff --git a/tests/multi_net/sslcontext_server_client_ciphers.py b/tests/multi_net/sslcontext_server_client_ciphers.py index 7017a6f5798ad..307ee5835a6b0 100644 --- a/tests/multi_net/sslcontext_server_client_ciphers.py +++ b/tests/multi_net/sslcontext_server_client_ciphers.py @@ -41,6 +41,10 @@ def instance0(): # Client def instance1(): + if not hasattr(tls, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) diff --git a/tests/multi_net/sslcontext_server_client_files.py b/tests/multi_net/sslcontext_server_client_files.py index 48ff6376fad9b..1267f43618a98 100644 --- a/tests/multi_net/sslcontext_server_client_files.py +++ b/tests/multi_net/sslcontext_server_client_files.py @@ -35,6 +35,10 @@ def instance0(): # Client def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) diff --git a/tests/multi_net/sslcontext_verify_callback.py b/tests/multi_net/sslcontext_verify_callback.py index 80056a5868c40..4339b46d85a5d 100644 --- a/tests/multi_net/sslcontext_verify_callback.py +++ b/tests/multi_net/sslcontext_verify_callback.py @@ -28,6 +28,10 @@ def verify_callback(cert, depth): # Server def instance0(): + if not hasattr(tls, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + multitest.globals(IP=multitest.get_network_ip()) s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) diff --git a/tests/multi_net/sslcontext_verify_error.py b/tests/multi_net/sslcontext_verify_error.py index 07dcc690b7ccc..a14d3c3b697bb 100644 --- a/tests/multi_net/sslcontext_verify_error.py +++ b/tests/multi_net/sslcontext_verify_error.py @@ -36,6 +36,10 @@ def instance0(): # Client def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) diff --git a/tests/multi_net/sslcontext_verify_time_error.py b/tests/multi_net/sslcontext_verify_time_error.py index 7b986322d15bd..a3d4ae1d1ef10 100644 --- a/tests/multi_net/sslcontext_verify_time_error.py +++ b/tests/multi_net/sslcontext_verify_time_error.py @@ -36,6 +36,10 @@ def instance0(): # Client def instance1(): + if not hasattr(ssl, "CERT_REQUIRED"): + print("SKIP") + raise SystemExit + multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) diff --git a/tests/multi_net/tls_dtls_server_client.py b/tests/multi_net/tls_dtls_server_client.py index ec7d7de55fdc4..10fac3c6f4269 100644 --- a/tests/multi_net/tls_dtls_server_client.py +++ b/tests/multi_net/tls_dtls_server_client.py @@ -7,6 +7,10 @@ print("SKIP") raise SystemExit +if not hasattr(tls, "PROTOCOL_DTLS_SERVER"): + print("SKIP") + raise SystemExit + PORT = 8000 # These are test certificates. See tests/README.md for details. From b2e376198331c3f61f19a59f711828eb5ec4c88b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 5 Aug 2025 10:01:04 +1000 Subject: [PATCH 14/34] tests/multi_net/udp_data_multi.py: Reduce number of UDP groups. On bare-metal lwIP targets, there's only enough buffer space for 4 groups. Signed-off-by: Damien George --- tests/multi_net/udp_data_multi.py | 2 +- tests/multi_net/udp_data_multi.py.exp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/multi_net/udp_data_multi.py b/tests/multi_net/udp_data_multi.py index 5d7b13e5188dd..a2ed33d292d78 100644 --- a/tests/multi_net/udp_data_multi.py +++ b/tests/multi_net/udp_data_multi.py @@ -5,7 +5,7 @@ NUM_NEW_SOCKETS = 4 NUM_PACKET_BURSTS = 6 -NUM_PACKET_GROUPS = 5 +NUM_PACKET_GROUPS = 4 TOTAL_PACKET_BURSTS = NUM_NEW_SOCKETS * NUM_PACKET_BURSTS # The tast passes if more than 75% of packets are received in each group. PACKET_RECV_THRESH = 0.75 * TOTAL_PACKET_BURSTS diff --git a/tests/multi_net/udp_data_multi.py.exp b/tests/multi_net/udp_data_multi.py.exp index bc67c6ab0cf01..a74ef42b7309c 100644 --- a/tests/multi_net/udp_data_multi.py.exp +++ b/tests/multi_net/udp_data_multi.py.exp @@ -7,7 +7,6 @@ pass group=0 pass group=1 pass group=2 pass group=3 -pass group=4 --- instance1 --- test socket 0 test socket 1 From 9d7ebcc411e0014ec613710406da54f280537004 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 5 Aug 2025 10:09:29 +1000 Subject: [PATCH 15/34] tests/run-perfbench.py: Skip misc_mandel if target doesn't have complex. Signed-off-by: Damien George --- tests/run-perfbench.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index 767700da0d54c..5a185699f4e56 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -100,7 +100,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): skip_reason = "" skip = ( skip_complex - and test_file.find("bm_fft") != -1 + and test_file.endswith(("bm_fft.py", "misc_mandel.py")) or skip_native and test_file.find("viper_") != -1 ) From 430ae9f0f74da3b68a3b00a1717c1ac0baa550cd Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 5 Aug 2025 10:18:14 +1000 Subject: [PATCH 16/34] esp32/modules: Convert AttributeError to ImportError. Signed-off-by: Damien George --- ports/esp32/modules/machine.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/esp32/modules/machine.py b/ports/esp32/modules/machine.py index 9cfda12f17753..eecdc14931a1a 100644 --- a/ports/esp32/modules/machine.py +++ b/ports/esp32/modules/machine.py @@ -189,4 +189,7 @@ def phases(self): # Delegate to built-in machine module. def __getattr__(attr): - return getattr(_machine, attr) + value = getattr(_machine, attr, None) + if value is None: + raise ImportError(attr) + return value From a54526498ec5316dcf110e819a8ba5fb8eb86566 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 6 Aug 2025 11:16:21 +1000 Subject: [PATCH 17/34] tests/ports/stm32/adcall.py: Print values if test ADC fails. Signed-off-by: Damien George --- tests/ports/stm32/adcall.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/ports/stm32/adcall.py b/tests/ports/stm32/adcall.py index 18896c40cb2a3..cf60aad04cc65 100644 --- a/tests/ports/stm32/adcall.py +++ b/tests/ports/stm32/adcall.py @@ -33,10 +33,14 @@ print(type(adc.read_channel(c))) # call special reading functions -print(skip_temp_test or 0 < adc.read_core_temp() < 100) -print(0 < adc.read_core_vbat() < 4) -print(0 < adc.read_core_vref() < 2) -print(0 < adc.read_vref() < 4) +core_temp = adc.read_core_temp() +core_vbat = adc.read_core_vbat() +core_vref = adc.read_core_vref() +vref = adc.read_vref() +print(skip_temp_test or 0 < core_temp < 100 or core_temp) +print(0 < core_vbat < 4 or core_vbat) +print(0 < core_vref < 2 or core_vref) +print(0 < vref < 4 or vref) if sys.implementation._build == "NUCLEO_WB55": # Restore button pin settings. From 9e4e073c9029c7465b7d80394530fe2085b0623c Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 10 Aug 2025 23:23:47 +1000 Subject: [PATCH 18/34] tests/extmod/machine_spi_rate.py: Support mimxrt and nrf ports. Signed-off-by: Damien George --- tests/extmod/machine_spi_rate.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/extmod/machine_spi_rate.py b/tests/extmod/machine_spi_rate.py index f78a089aae7e9..9a558f46fcbf4 100644 --- a/tests/extmod/machine_spi_rate.py +++ b/tests/extmod/machine_spi_rate.py @@ -35,6 +35,12 @@ elif "esp8266" in sys.platform: MAX_DELTA_MS = 50 # port requires much looser timing requirements spi_instances = ((1, None, None, None),) # explicit pin choice not allowed +elif "mimxrt" in sys.platform: + # Use default SPI instance (tested working on TEENSY40). + spi_instances = ((None, None, None, None),) +elif "nrf" in sys.platform: + # Tested working on ARDUINO_NANO_33_BLE_SENSE. + spi_instances = ((1, None, None, None),) else: print("Please add support for this test on this platform.") raise SystemExit From dbfeb321e3254b5a19f54e97114277bc994f718f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Aug 2025 10:49:44 +1000 Subject: [PATCH 19/34] tests/run-tests.py: Abort test if serial port fails at start. The test runner will abort with error code 2 if: - the target could not be detected (enter_raw_repl failed at the detection stage) - unittest is not installed but is needed Signed-off-by: Damien George --- tests/run-tests.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 3a86bc324d547..3bd6dfd670f65 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -295,7 +295,12 @@ def get_test_instance(test_instance, baudrate, user, password): pyb = pyboard.Pyboard(port, baudrate, user, password) pyboard.Pyboard.run_script_on_remote_target = run_script_on_remote_target - pyb.enter_raw_repl() + try: + pyb.enter_raw_repl() + except pyboard.PyboardError as e: + print("error: could not detect test instance") + print(e) + sys.exit(2) return pyb @@ -1145,7 +1150,7 @@ def run_one_test(test_file): except TestError as er: for line in er.args[0]: print(line) - sys.exit(1) + sys.exit(2) # Return test results. return test_results.value, testcase_count.value From 863dfd55e1f827c124ce963bec1497a4ce0ea12d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Aug 2025 00:01:55 +1000 Subject: [PATCH 20/34] tests/run-tests.py: Abort test run if enter_raw_repl fails many times. Now, if a test fails and crashes a board to the point it no longer responds (but the serial connection is still alive and able to read/write), it only takes a maximum of 30 + 5 * 4 = 50 seconds to detect that state and exit the test run (previously it would be 30 + 10 seconds * number of remaining tests = potentially large). Can test this by adding the following to a test: import time, micropython micropython.kbd_intr(-1) while 1: time.sleep(1) Signed-off-by: Damien George --- tests/run-tests.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 3bd6dfd670f65..9279cefe6999c 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -443,14 +443,17 @@ def run_script_on_remote_target(pyb, args, test_file, is_special): return True, script try: - had_crash = False - pyb.enter_raw_repl() + pyb.enter_raw_repl(timeout_overall=4) if test_file.endswith(tests_requiring_target_wiring) and pyb.target_wiring_script: pyb.exec_( "import sys;sys.modules['target_wiring']=__build_class__(lambda:exec(" + repr(pyb.target_wiring_script) + "),'target_wiring')" ) + except pyboard.PyboardError as e: + return True, b"enter_raw_repl failed\n" + + try: output_mupy = pyb.exec_(script, timeout=TEST_TIMEOUT) except pyboard.PyboardError as e: had_crash = True @@ -743,6 +746,7 @@ def run_script_on_remote_target(self, args, test_file, is_special): def run_tests(pyb, tests, args, result_dir, num_threads=1): testcase_count = ThreadSafeCounter() + enter_raw_repl_failure_count = ThreadSafeCounter() test_results = ThreadSafeCounter([]) skip_tests = set() @@ -1117,6 +1121,9 @@ def run_one_test(test_file): rm_f(filename_expected) rm_f(filename_mupy) else: + if output_mupy == b"enter_raw_repl failed\n": + extra_info = "enter_raw_repl failed" + enter_raw_repl_failure_count.increment() print("FAIL ", test_file, extra_info) if output_expected is not None: with open(filename_expected, "wb") as f: @@ -1147,6 +1154,9 @@ def run_one_test(test_file): else: for test in tests: run_one_test(test) + if enter_raw_repl_failure_count.value >= 4: + print("Too many enter_raw_repl failures, aborting test run") + break except TestError as er: for line in er.args[0]: print(line) From ff9f6f91320ee1e97538578373bb6561ffdad2a2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Aug 2025 19:21:46 +1000 Subject: [PATCH 21/34] esp8266/boards: Include unittest in 512k board variant. Signed-off-by: Damien George --- .../boards/ESP8266_GENERIC/manifest_512kiB.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ports/esp8266/boards/ESP8266_GENERIC/manifest_512kiB.py b/ports/esp8266/boards/ESP8266_GENERIC/manifest_512kiB.py index 15f6cffc3f94a..53538844b4cc6 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/manifest_512kiB.py +++ b/ports/esp8266/boards/ESP8266_GENERIC/manifest_512kiB.py @@ -1,9 +1,10 @@ module("_boot.py", opt=3) -module("apa102.py", base_path="$(PORT_DIR)/modules", opt=3) -module("port_diag.py", base_path="$(PORT_DIR)/modules", opt=3) -require("ntptime") -require("dht") -require("onewire") -require("ds18x20") -require("webrepl") -require("neopixel") +# module("apa102.py", base_path="$(PORT_DIR)/modules", opt=3) +# module("port_diag.py", base_path="$(PORT_DIR)/modules", opt=3) +# require("ntptime") +# require("dht") +# require("onewire") +# require("ds18x20") +# require("webrepl") +# require("neopixel") +require("unittest") From 81ef9848f814cef18534359c9d28b40ff10983f7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Aug 2025 11:35:10 +1000 Subject: [PATCH 22/34] tests/run-natmodtests.py: Abort test run if target fails. Signed-off-by: Damien George --- tests/run-natmodtests.py | 44 ++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index 50e8d1272914e..5b58f9a0e2699 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -77,6 +77,10 @@ def open(self, path, mode): """ +class TargetError(Exception): + pass + + class TargetSubprocess: def __init__(self, cmd): self.cmd = cmd @@ -97,7 +101,10 @@ def run_script(self, script): class TargetPyboard: def __init__(self, pyb): self.pyb = pyb - self.pyb.enter_raw_repl() + try: + self.pyb.enter_raw_repl(timeout_overall=4) + except pyboard.PyboardError as er: + raise TargetError(str(er)) def close(self): self.pyb.exit_raw_repl() @@ -105,7 +112,7 @@ def close(self): def run_script(self, script): try: - self.pyb.enter_raw_repl() + self.pyb.enter_raw_repl(timeout_overall=4) output = self.pyb.exec_(script) output = output.replace(b"\r\n", b"\n") return output, None @@ -140,6 +147,7 @@ def run_tests(target_truth, target, args, resolved_arch): prelude = args.begin.read() injected_import_hook_code = injected_import_hook_code.replace("{import_prelude}", prelude) + enter_raw_repl_failure_count = 0 test_results = [] for test_file in args.files: # Find supported test @@ -219,6 +227,12 @@ def run_tests(target_truth, target, args, resolved_arch): # Print result print("{:4} {}{}".format(result, test_file, extra)) + if result == "FAIL" and str(error) == "could not enter raw repl": + enter_raw_repl_failure_count += 1 + if enter_raw_repl_failure_count >= 4: + print("Too many enter_raw_repl failures, aborting test run") + break + return test_results @@ -253,17 +267,21 @@ def main(): cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() - target_truth = TargetSubprocess([CPYTHON3]) + try: + target_truth = TargetSubprocess([CPYTHON3]) - target = run_tests_module.get_test_instance( - args.test_instance, args.baudrate, args.user, args.password - ) - if target is None: - # Use the unix port of MicroPython. - target = TargetSubprocess([MICROPYTHON]) - else: - # Use a remote target. - target = TargetPyboard(target) + target = run_tests_module.get_test_instance( + args.test_instance, args.baudrate, args.user, args.password + ) + if target is None: + # Use the unix port of MicroPython. + target = TargetSubprocess([MICROPYTHON]) + else: + # Use a remote target. + target = TargetPyboard(target) + except TargetError as er: + print("Cannot initialise targets: {}".format(er)) + sys.exit(2) if hasattr(args, "arch") and args.arch is not None: target_arch = args.arch @@ -272,7 +290,7 @@ def main(): target_platform, target_arch, error = detect_architecture(target) if error: print("Cannot run tests: {}".format(error)) - sys.exit(1) + sys.exit(2) target_arch = ARCH_MAPPINGS.get(target_arch, target_arch) if target_platform: From 15de2a37f79d61fbb912718285f653d79768c456 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 17 Aug 2025 00:34:38 +1000 Subject: [PATCH 23/34] tests: Move esp32-specific test to general test dir. Because it needs internet connection it must be in a test directory that is expected to have internet connection. Also, this test can be made to run on more targets in the future. Signed-off-by: Damien George --- tests/{ports/esp32 => net_inet}/resolve_on_connect.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename tests/{ports/esp32 => net_inet}/resolve_on_connect.py (91%) diff --git a/tests/ports/esp32/resolve_on_connect.py b/tests/net_inet/resolve_on_connect.py similarity index 91% rename from tests/ports/esp32/resolve_on_connect.py rename to tests/net_inet/resolve_on_connect.py index e604ce9ca099a..54d712c2447de 100644 --- a/tests/ports/esp32/resolve_on_connect.py +++ b/tests/net_inet/resolve_on_connect.py @@ -1,4 +1,6 @@ -# Test that the esp32's socket module performs DNS resolutions on bind and connect +# Test that the socket module performs DNS resolutions on bind and connect +# Only works on the esp32 port at the moment + import sys if sys.implementation.name == "micropython" and sys.platform != "esp32": From e8e39d78c354a915d72730ead9074a18af3afa0a Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 17 Aug 2025 00:42:17 +1000 Subject: [PATCH 24/34] tests/net_inet/resolve_on_connect.py: Convert to use unittest. Signed-off-by: Damien George --- tests/net_inet/resolve_on_connect.py | 50 +++++++++------------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/tests/net_inet/resolve_on_connect.py b/tests/net_inet/resolve_on_connect.py index 54d712c2447de..587104910e48f 100644 --- a/tests/net_inet/resolve_on_connect.py +++ b/tests/net_inet/resolve_on_connect.py @@ -7,52 +7,32 @@ print("SKIP") raise SystemExit -import socket, sys +import socket +import unittest -def test_bind_resolves_0_0_0_0(): - s = socket.socket() - try: +class Test(unittest.TestCase): + def test_bind_resolves_0_0_0_0(self): + s = socket.socket() s.bind(("0.0.0.0", 31245)) - print("bind actually bound") s.close() - except Exception as e: - print("bind raised", e) - -def test_bind_resolves_localhost(): - s = socket.socket() - try: + def test_bind_resolves_localhost(self): + s = socket.socket() s.bind(("localhost", 31245)) - print("bind actually bound") s.close() - except Exception as e: - print("bind raised", e) - -def test_connect_resolves(): - s = socket.socket() - try: + def test_connect_resolves(self): + s = socket.socket() s.connect(("micropython.org", 80)) - print("connect actually connected") s.close() - except Exception as e: - print("connect raised", e) - -def test_connect_non_existent(): - s = socket.socket() - try: - s.connect(("nonexistent.example.com", 80)) - print("connect actually connected") + def test_connect_non_existent(self): + s = socket.socket() + with self.assertRaises(OSError): + s.connect(("nonexistent.example.com", 80)) s.close() - except OSError as e: - print("connect raised OSError") - except Exception as e: - print("connect raised", e) -test_funs = [n for n in dir() if n.startswith("test_")] -for f in sorted(test_funs): - print("--", f, end=": ") - eval(f + "()") +if __name__ == "__main__": + unittest.main() From 8644b7b4daa0291384af4c46c58d2b5b6066d322 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 17 Aug 2025 18:58:29 +1000 Subject: [PATCH 25/34] tests/ports/esp32/check_err_str.py: Open file in root directory. Otherwise the test will fail with --via-mpy because the mounted __FS object will allow any file to be opened. Signed-off-by: Damien George --- tests/ports/esp32/check_err_str.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ports/esp32/check_err_str.py b/tests/ports/esp32/check_err_str.py index 055eecf7476ae..382fc27d5378e 100644 --- a/tests/ports/esp32/check_err_str.py +++ b/tests/ports/esp32/check_err_str.py @@ -7,7 +7,7 @@ # try some vanilla OSError to get std error code try: - open("this filedoesnotexist", "r") + open("/this filedoesnotexist", "r") print("FAILED TO RAISE") except OSError as e: print(e) From d31ac0733b751a03aebbf3911a502a392bd4a985 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 17 Aug 2025 19:19:47 +1000 Subject: [PATCH 26/34] tests/misc/rge_sm.py: Use list.append instead of list += [...]. To reduce heap memory use. Signed-off-by: Damien George --- tests/misc/rge_sm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misc/rge_sm.py b/tests/misc/rge_sm.py index 56dad5749776e..6084574e70aa6 100644 --- a/tests/misc/rge_sm.py +++ b/tests/misc/rge_sm.py @@ -29,7 +29,7 @@ def iterate(self): k = ktmp[:] step = [s + c * k[ik] for ik, s in enumerate(step)] if self.save: - self.Trajectory += [step] + self.Trajectory.append(step) else: self.Trajectory = [step] return True From 8d04fae7847028136d97531e78ff9ac544a15d8b Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 17 Aug 2025 19:20:19 +1000 Subject: [PATCH 27/34] py/objlist: Make list append allocation more gentle. Signed-off-by: Damien George --- py/objlist.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/py/objlist.c b/py/objlist.c index 71414a849f40b..ef2262fe9e49c 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -229,8 +229,10 @@ mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); if (self->len >= self->alloc) { - self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc * 2); - self->alloc *= 2; + // Progression: 4 8 12 20 32 48 72 108 164 248 372 ... + size_t new_alloc = (self->alloc + self->alloc / 2 + 3) & ~3; + self->items = m_renew(mp_obj_t, self->items, self->alloc, new_alloc); + self->alloc = new_alloc; mp_seq_clear(self->items, self->len + 1, self->alloc, sizeof(*self->items)); } self->items[self->len++] = arg; From 38c8e0b6abf7d879bde58999a4fa1fb404b0b7fb Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 22 Sep 2025 14:15:46 +1000 Subject: [PATCH 28/34] tests/run-tests.py: Auto detect -march-flags for mpy-cross. Signed-off-by: Damien George --- tests/run-tests.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 9279cefe6999c..56ab93a823966 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -308,8 +308,13 @@ def detect_inline_asm_arch(pyb, args): for arch in ("rv32", "thumb", "xtensa"): output = run_feature_check(pyb, args, "inlineasm_{}.py".format(arch)) if output.strip() == arch.encode(): - return arch - return None + arch_flags = None + if arch == "rv32": + output = run_feature_check(pyb, args, "inlineasm_rv32_zba.py") + if output == b"rv32_zba\n": + arch_flags = "zba" + return arch, arch_flags + return None, None def detect_test_platform(pyb, args): @@ -320,7 +325,7 @@ def detect_test_platform(pyb, args): platform, arch, build, thread, float_prec, unicode = str(output, "ascii").strip().split() if arch == "None": arch = None - inlineasm_arch = detect_inline_asm_arch(pyb, args) + inlineasm_arch, arch_flags = detect_inline_asm_arch(pyb, args) if thread == "None": thread = None float_prec = int(float_prec) @@ -328,8 +333,11 @@ def detect_test_platform(pyb, args): args.platform = platform args.arch = arch + args.arch_flags = arch_flags if arch and not args.mpy_cross_flags: args.mpy_cross_flags = "-march=" + arch + if arch_flags: + args.mpy_cross_flags += " -march-flags=" + arch_flags args.inlineasm_arch = inlineasm_arch args.build = build args.thread = thread From 60adac56fcd13d6d61a37f79e552a62eac00c225 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 23 Sep 2025 16:54:45 +1000 Subject: [PATCH 29/34] tests/run-tests.py: Allow port: specification in run-multitests.py. This moves the `port:` check for the test instance to the `convert_device_shortcut_to_real_device()` helper function, which means that `run-multitests.py` can use this specification. Signed-off-by: Damien George --- tests/run-tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index 56ab93a823966..3c324a2906ac2 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -268,7 +268,9 @@ def platform_to_port(platform): def convert_device_shortcut_to_real_device(device): - if device.startswith("a") and device[1:].isdigit(): + if device.startswith("port:"): + return device.split(":", 1)[1] + elif device.startswith("a") and device[1:].isdigit(): return "/dev/ttyACM" + device[1:] elif device.startswith("u") and device[1:].isdigit(): return "/dev/ttyUSB" + device[1:] @@ -279,9 +281,7 @@ def convert_device_shortcut_to_real_device(device): def get_test_instance(test_instance, baudrate, user, password): - if test_instance.startswith("port:"): - _, port = test_instance.split(":", 1) - elif test_instance == "unix": + if test_instance == "unix": return None elif test_instance == "webassembly": return PyboardNodeRunner() From 64c8c24b82813d12afad75978ea28985e53c5ca7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Sep 2025 15:19:48 +1000 Subject: [PATCH 30/34] tests/extmod/time_res.py: Properly skip functions not in time module. If `time.time` doesn't exist, it tries to use `globals()['time']` which is the time module itself, and that causes the test to fail. Instead it should just skip `time.time`. Signed-off-by: Damien George --- tests/extmod/time_res.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/extmod/time_res.py b/tests/extmod/time_res.py index 548bef1f1747c..ef20050b914d7 100644 --- a/tests/extmod/time_res.py +++ b/tests/extmod/time_res.py @@ -37,9 +37,12 @@ def test(): time.sleep_ms(100) for func_name, _ in EXPECTED_MAP: try: - time_func = getattr(time, func_name, None) or globals()[func_name] + if func_name.endswith("_time"): + time_func = globals()[func_name] + else: + time_func = getattr(time, func_name) now = time_func() # may raise AttributeError - except (KeyError, AttributeError): + except AttributeError: continue try: results_map[func_name].add(now) From 347e8e10be0b516cd6703700106055f659db5e5d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Sep 2025 22:37:10 +1000 Subject: [PATCH 31/34] tests/net_inet: Update micropython.org certificate for SSL tests. Signed-off-by: Damien George --- tests/net_inet/mpycert.der | Bin 1290 -> 1290 bytes tests/net_inet/ssl_cert.py | 52 ++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/net_inet/mpycert.der b/tests/net_inet/mpycert.der index 0b0eabc9bc8135de25785cb8574ab78e03947aa9..f583b217ae5d960d2ba534fbbbf2a5b1e492777f 100644 GIT binary patch delta 850 zcmV-Y1Figu3W^FKFoFdJFoFW^paTK{0s;{L!V)q|aH+JBaoj-n;)NzWks!1IGLh9G zf7+NRbgjOW{yX3v;^j+qt1J?K?WY4Wem0lv=rNC_6O9X*E*{MRQ9VrMC~gT=F{-5^ zU2^m-2f|H2Jl_E$xp+7A8f7vx=7w>d1n-0hTfG7rnd_zj0Ge}b)w~n8%xOf2%um4?NqlV7^*QP8jLiDsxUZei#+W?``^y3*#^6q-} zC!XcaoGqgAf1DSs?-MsU(X_}cLDq)A>Fgo+SsQ=3I((m80i9w5!YI88tvt#Q0_+=; z_5oQG0JSOdEsk$7=$lNfcs}~v=?u}*lVbv>e~)$03D0VFNM$mT-nB%?g?986gp2~e zcI&h8;`wfbs8*5Tl1umD>Hv61k9Csc&p>jJ?- z@c8vkWFUK}<1xg*XbDG)*tvM1%1%nYX(TF3H+xY7YdSwti?h_ytyoTz?#o|q4KYbb zy2L-963HMv5rhYYwD|HkBdl=%K<9sre?~NdYEIAK4r5=sX_`j~5F-9dNVk!7RLhed z*h-7GdzuHqL{v@f1|5kwp8d%mZ_}suv*I-dNz|Jo6cBLfS)xA zUM;_Ln_?yxoRKjp3-f=?!(xkQ$9MF93;V`d^#f*0rcj&E@1^_#v|vpGOJPsNe;@~o zNU$;Www%$)t#=sQvd0g;;6@1E;7i`QxVFa@zBXw;hGP#cFy2ti?#B4t-r= zx2WvF=ks!1IF_G0F zf4YafTpIv6%gPGP)MpU+0X(HnrD4k^%2N9k0k^p*^(Y?gC$l6NIcEtB2<+fGuse4S zz4PTcR6z_*u0htBbly?h?|c7Py{NYn_TDko@^Z;?Y72vOm7GQZWE7e6vDqTo&8U*9 z942}{{_j`?dkB?*+i?$$`{OYQy9vHfe@$PHk#;>=ot}g8>BMHU;Ra?yNEm=+pf?zr zn}jl#v9i6y_!R)1Cy4vvV;7cenxdX~KE%NnGG0P~pV9p#`jaKy3iD!a+Z$ zShz36T$`o437+x(>#&76{iq7>MZ|dPWh%nYUp(I9TumGI3nwe83qp5OPkCw`0*Bd? z_5oQG#m`2h>h#0FdTf=%EwEmaFDB!;lVbv>e@^0wT?zvnkT}rwANg@y_`{E~H;8&~ zuq7YuzPH$h29D_ogw1llf%E*z&r?Y4z4mEB)TI$VHOLdPk{tzK>MqpS_({X9^_iBO z?8TQTRbT9 z47O}D2u;^G?MN*y1!%5-UGGfX*dRe)Xz{yR&EpQR3^9kBL*7Y4;m8C((L5W6MKEco zsd5f^5*#tuJ|kD%r%#~p9D&hyA8*#uxu~-P7J)fSpH;5XYUH!O>ZR46OQXrwe>-yX zO7>K%4E#9vV1VClHOh6NROBzbZWqZKr8l*rn!4=tz;#AXcc|!DR*_~cZr&+@)~1&Q zUSvhSEY9j=LPW5`Pp$B@+yly)n+bxIf(>JeX89R%_|>_I5aNlL2{rkm9KMosBXsGV cAJ;odnuJ&o!t/dev/null # The certificate is from Let's Encrypt: -# 1 s:C=US, O=Let's Encrypt, CN=R11 +# 1 s:C=US, O=Let's Encrypt, CN=R12 # i:C=US, O=Internet Security Research Group, CN=ISRG Root X1 # a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption # v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT @@ -17,39 +17,39 @@ # Then convert to hex format using: for i in range(0,len(data),40):print(data[i:i+40].hex()) ca_cert_chain = bytes.fromhex( - "30820506308202eea0030201020211008a7d3e13d62f30ef2386bd29076b34f8300d06092a864886" + "30820506308202eea003020102021100c212324b70a9b49171dc40f7e285263c300d06092a864886" "f70d01010b0500304f310b300906035504061302555331293027060355040a1320496e7465726e65" "742053656375726974792052657365617263682047726f7570311530130603550403130c49535247" "20526f6f74205831301e170d3234303331333030303030305a170d3237303331323233353935395a" "3033310b300906035504061302555331163014060355040a130d4c6574277320456e637279707431" - "0c300a0603550403130352313130820122300d06092a864886f70d01010105000382010f00308201" - "0a0282010100ba87bc5c1b0039cbca0acdd46710f9013ca54ea561cb26ca52fb1501b7b928f5281e" - "ed27b324183967090c08ece03ab03b770ebdf3e53954410c4eae41d69974de51dbef7bff58bda8b7" - "13f6de31d5f272c9726a0b8374959c4600641499f3b1d922d9cda892aa1c267a3ffeef58057b0895" - "81db710f8efbe33109bb09be504d5f8f91763d5a9d9e83f2e9c466b3e106664348188065a037189a" - "9b843297b1b2bdc4f815009d2788fbe26317966c9b27674bc4db285e69c279f0495ce02450e1c4bc" - "a105ac7b406d00b4c2413fa758b82fc55c9ba5bb099ef1feebb08539fda80aef45c478eb652ac2cf" - "5f3cdee35c4d1bf70b272baa0b4277534f796a1d87d90203010001a381f83081f5300e0603551d0f" + "0c300a0603550403130352313230820122300d06092a864886f70d01010105000382010f00308201" + "0a0282010100da982874adbe94fe3be01ee2e54b75ab2c127feda703327e3697ece8318fa5138d0b" + "992e1ecd01513d4ce5286e095531aaa5225d72f42d07c24d403cdf0123b97837f51a653234e68671" + "9d04ef84085bbd021a99eba601009a73906d8fa207a0d097d3da456181353d14f9c4c05f6adc0b96" + "1ab09fe32aeabd2ad698c79b71ab3b740f3cdbb260be5a4b4e18e9db2a735c8961659efeed3ca6cb" + "4e6fe49ef90046b3ff194d2a63b38e66c6188570c750656f3b74e548830f08585d2d239d5ea3fee8" + "db00a1d2f4e3194df2ee7af6279ee5cd9c2da2f27f9c17adef133739d1b4c82c41d686c0e9ec21f8" + "591b7fb93a7c9f5c019d6204c228bd0aad3cca10ec1b0203010001a381f83081f5300e0603551d0f" "0101ff040403020186301d0603551d250416301406082b0601050507030206082b06010505070301" - "30120603551d130101ff040830060101ff020100301d0603551d0e04160414c5cf46a4eaf4c3c07a" - "6c95c42db05e922f26e3b9301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58" + "30120603551d130101ff040830060101ff020100301d0603551d0e0416041400b529f22d8e6f31e8" + "9b4cad783efadce90cd1d2301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58" "f6e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a2f" "2f78312e692e6c656e63722e6f72672f30130603551d20040c300a3008060667810c010201302706" "03551d1f0420301e301ca01aa0188616687474703a2f2f78312e632e6c656e63722e6f72672f300d" - "06092a864886f70d01010b050003820201004ee2895d0a031c9038d0f51ff9715cf8c38fb237887a" - "6fb0251fedbeb7d886068ee90984cd72bf81f3fccacf5348edbdf66942d4a5113e35c813b2921d05" - "5fea2ed4d8f849c3adf599969cef26d8e1b4240b48204dfcd354b4a9c621c8e1361bff77642917b9" - "f04bef5deacd79d0bf90bfbe23b290da4aa9483174a9440be1e2f62d8371a4757bd294c10519461c" - "b98ff3c47448252a0de5f5db43e2db939bb919b41f2fdf6a0e8f31d3630fbb29dcdd662c3fb01b67" - "51f8413ce44db9acb8a49c6663f5ab85231dcc53b6ab71aedcc50171da36ee0a182a32fd09317c8f" - "f673e79c9cb54a156a77825acfda8d45fe1f2a6405303e73c2c60cb9d63b634aab4603fe99c04640" - "276063df503a0747d8154a9fea471f995a08620cb66c33084dd738ed482d2e0568ae805def4cdcd8" - "20415f68f1bb5acde30eb00c31879b43de4943e1c8043fd13c1b87453069a8a9720e79121c31d83e" - "2357dda74fa0f01c81d1771f6fd6d2b9a8b3031681394b9f55aed26ae4b3bfeaa5d59f4ba3c9d63b" - "72f34af654ab0cfc38f76080df6e35ca75a154e42fbc6e17c91aa537b5a29abaecf4c075464f77a8" - "e8595691662d6ede2981d6a697055e6445be2cceea644244b0c34fadf0b4dc03ca999b098295820d" - "638a66f91972f8d5b98910e289980935f9a21cbe92732374e99d1fd73b4a9a845810c2f3a7e235ec" - "7e3b45ce3046526bc0c0" + "06092a864886f70d01010b050003820201008f75d009cf6a7648653292deb544c88576f415848c02" + "bf76ebb3f1e2f96e84a85691e1924bf7e1ea0078488f7592e3e4467b1b602b20afa0ce14e5450d6a" + "e05286a4f3da1414a9a95ff16d46f952501740e9e41e7de61558fea98bfceff59e63e066e2c3773b" + "1f01872694ed4010dcb799ecdd57d35c7141ee30200004dc954b5028879992feaa8094b6060814f8" + "1c837e7440c5085a0c4f5cd1849dc4fddb59deee796e234d95f292d498296a5ceb02c142f0f8f54e" + "64207ba8e331c4c06809478bd8b978a0ca4e4abe69242a4b377b51036b3a3f528bb3d4d2ad584e93" + "eecb5f6f0d314948bac43f9f12c9203d11840785b4f8f23823ac710040e77f8d4634826a4ecfe00e" + "635fba699a47091022fe4b48b7917554cb931ee416eb53cf7bde364dbff6b1ebe64ae9333c8d69a2" + "98bea87fa3ab5fb654e84d96a9acf3b05acb1b7a3693249bce5852809f350a5e2dbf749b6226179c" + "9131290bf37fcdc3628b68c777f47f0bfbc659f503664ba6509bd0efa5fc02b4604d034b614fc520" + "078b48b031f5b69cd1c9ad7718dcb2c70fbee04608dee04bdeb9b8b6c716be36693f86684b748113" + "8950c56a7a02acc548a50e7d5d61e4cdd166a075c7055ee889b5631923bb50b490ecc275373e75a6" + "1b83252800214ec0d33acb9ceac08ff75fae51164610af0206eec0b657d40dac8cd8d7a0f3876ec3" + "e2cbe94ed4a17cfd763b" ) From 98fddbc9247698cd8861906a7f6cf5c11c6d7f93 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Sep 2025 22:37:40 +1000 Subject: [PATCH 32/34] tests/extmod/machine_soft_timer.py: Skip on nrf boards. They don't have the `freq` argument. Signed-off-by: Damien George --- tests/extmod/machine_soft_timer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/extmod/machine_soft_timer.py b/tests/extmod/machine_soft_timer.py index 1e9c66aa225e1..6f1ff6e58ef1b 100644 --- a/tests/extmod/machine_soft_timer.py +++ b/tests/extmod/machine_soft_timer.py @@ -2,7 +2,7 @@ import sys -if sys.platform in ("esp32", "esp8266"): +if sys.platform in ("esp32", "esp8266", "nrf"): print("SKIP") # TODO: Implement soft timers for esp32/esp8266 ports raise SystemExit From 62f5dcf40eb97da2a2d532a04199b1d061141402 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Sep 2025 22:38:01 +1000 Subject: [PATCH 33/34] tests/micropython/ringio_big.py: Print results at end of test. So it can be properly skipped. Signed-off-by: Damien George --- tests/micropython/ringio_big.py | 8 ++++++-- tests/micropython/ringio_big.py.exp | 3 +-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/micropython/ringio_big.py b/tests/micropython/ringio_big.py index d55c4c00b7c0d..198a87a08a5e5 100644 --- a/tests/micropython/ringio_big.py +++ b/tests/micropython/ringio_big.py @@ -8,6 +8,8 @@ print("SKIP") raise SystemExit +result = [] + try: # The maximum possible size micropython.RingIO(bytearray(65535)) @@ -17,13 +19,15 @@ # Buffer may not be too big micropython.RingIO(bytearray(65536)) except ValueError as ex: - print(type(ex)) + result.append(type(ex)) try: # Size may not be too big micropython.RingIO(65535) except ValueError as ex: - print(type(ex)) + result.append(type(ex)) except MemoryError: print("SKIP") raise SystemExit + +print(result) diff --git a/tests/micropython/ringio_big.py.exp b/tests/micropython/ringio_big.py.exp index 72af34b383872..3355e89611958 100644 --- a/tests/micropython/ringio_big.py.exp +++ b/tests/micropython/ringio_big.py.exp @@ -1,2 +1 @@ - - +[, ] From 3e0007308b78893179ce37f9d6a1af344d754454 Mon Sep 17 00:00:00 2001 From: Hans Maerki Date: Mon, 20 Oct 2025 19:35:05 +0200 Subject: [PATCH 34/34] proposal: run-multitests.py --target-wiring --- tests/multi_extmod/machine_i2c_target_irq.py | 46 ++++++----- .../multi_extmod/machine_i2c_target_memory.py | 52 ++++++------ tests/run-multitests.py | 81 +++++++++++++++---- tests/run-tests.py | 17 +++- tests/target_wiring/rp2.py | 4 + tests/target_wiring/rp2_octoprobe_infra.py | 5 ++ 6 files changed, 137 insertions(+), 68 deletions(-) create mode 100644 tests/target_wiring/rp2_octoprobe_infra.py diff --git a/tests/multi_extmod/machine_i2c_target_irq.py b/tests/multi_extmod/machine_i2c_target_irq.py index eafd9dfdca838..727daf31d71ff 100644 --- a/tests/multi_extmod/machine_i2c_target_irq.py +++ b/tests/multi_extmod/machine_i2c_target_irq.py @@ -15,32 +15,34 @@ print("SKIP") raise SystemExit +from target_wiring import i2c_args, i2c_kwargs + ADDR = 67 clock_stretch_us = 200 # Configure pins based on the target. -if sys.platform == "alif": - i2c_args = (1,) # pins P3_7/P3_6 - i2c_kwargs = {} -elif sys.platform == "mimxrt": - i2c_args = (0,) # pins 19/18 on Teensy 4.x - i2c_kwargs = {} - clock_stretch_us = 50 # mimxrt cannot delay too long in the IRQ handler -elif sys.platform == "rp2": - i2c_args = (0,) - i2c_kwargs = {"scl": 9, "sda": 8} -elif sys.platform == "pyboard": - i2c_args = ("Y",) - i2c_kwargs = {} -elif sys.platform == "samd": - i2c_args = () # pins SCL/SDA - i2c_kwargs = {} -elif "zephyr-rpi_pico" in sys.implementation._machine: - i2c_args = ("i2c1",) # on gpio7/gpio6 - i2c_kwargs = {} -else: - print("Please add support for this test on this platform.") - raise SystemExit +# if sys.platform == "alif": +# i2c_args = (1,) # pins P3_7/P3_6 +# i2c_kwargs = {} +# elif sys.platform == "mimxrt": +# i2c_args = (0,) # pins 19/18 on Teensy 4.x +# i2c_kwargs = {} +# clock_stretch_us = 50 # mimxrt cannot delay too long in the IRQ handler +# elif sys.platform == "rp2": +# i2c_args = (0,) +# i2c_kwargs = {"scl": 9, "sda": 8} +# elif sys.platform == "pyboard": +# i2c_args = ("Y",) +# i2c_kwargs = {} +# elif sys.platform == "samd": +# i2c_args = () # pins SCL/SDA +# i2c_kwargs = {} +# elif "zephyr-rpi_pico" in sys.implementation._machine: +# i2c_args = ("i2c1",) # on gpio7/gpio6 +# i2c_kwargs = {} +# else: +# print("Please add support for this test on this platform.") +# raise SystemExit def simple_irq(i2c_target): diff --git a/tests/multi_extmod/machine_i2c_target_memory.py b/tests/multi_extmod/machine_i2c_target_memory.py index 6b3f0d03eb7fe..8796ec47a34b9 100644 --- a/tests/multi_extmod/machine_i2c_target_memory.py +++ b/tests/multi_extmod/machine_i2c_target_memory.py @@ -10,33 +10,35 @@ import sys from machine import I2C, I2CTarget +from target_wiring import i2c_args, i2c_kwargs + ADDR = 67 -# Configure pins based on the target. -if sys.platform == "alif": - i2c_args = (1,) # pins P3_7/P3_6 - i2c_kwargs = {} -elif sys.platform == "esp32": - i2c_args = (1,) # on pins 9/8 - i2c_kwargs = {} -elif sys.platform == "mimxrt": - i2c_args = (0,) # pins 19/18 on Teensy 4.x - i2c_kwargs = {} -elif sys.platform == "rp2": - i2c_args = (0,) - i2c_kwargs = {"scl": 9, "sda": 8} -elif sys.platform == "pyboard": - i2c_args = ("Y",) - i2c_kwargs = {} -elif sys.platform == "samd": - i2c_args = () # pins SCL/SDA - i2c_kwargs = {} -elif "zephyr-rpi_pico" in sys.implementation._machine: - i2c_args = ("i2c1",) # on gpio7/gpio6 - i2c_kwargs = {} -else: - print("Please add support for this test on this platform.") - raise SystemExit +# # Configure pins based on the target. +# if sys.platform == "alif": +# i2c_args = (1,) # pins P3_7/P3_6 +# i2c_kwargs = {} +# elif sys.platform == "esp32": +# i2c_args = (1,) # on pins 9/8 +# i2c_kwargs = {} +# elif sys.platform == "mimxrt": +# i2c_args = (0,) # pins 19/18 on Teensy 4.x +# i2c_kwargs = {} +# elif sys.platform == "rp2": +# i2c_args = (0,) +# i2c_kwargs = {"scl": 9, "sda": 8} +# elif sys.platform == "pyboard": +# i2c_args = ("Y",) +# i2c_kwargs = {} +# elif sys.platform == "samd": +# i2c_args = () # pins SCL/SDA +# i2c_kwargs = {} +# elif "zephyr-rpi_pico" in sys.implementation._machine: +# i2c_args = ("i2c1",) # on gpio7/gpio6 +# i2c_kwargs = {} +# else: +# print("Please add support for this test on this platform.") +# raise SystemExit def simple_irq(i2c_target): diff --git a/tests/run-multitests.py b/tests/run-multitests.py index e5458ffe0d00c..5ca3a005999f6 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -8,12 +8,13 @@ # Runs a test suite that relies on two micropython instances/devices # interacting in some way. Typically used to test networking / bluetooth etc. - +from __future__ import annotations import sys, os, time, re, select import argparse import itertools import subprocess import tempfile +import dataclasses run_tests_module = __import__("run-tests") @@ -264,6 +265,9 @@ def run_script(self, script): def start_script(self, script): self.pyb.enter_raw_repl() + if hasattr(self, 'target_wiring_script'): + cmd = "import sys;sys.modules['target_wiring']=__build_class__(lambda:exec(" + repr(self.target_wiring_script) + "),'target_wiring')" + self.pyb.exec_(cmd) self.pyb.exec_raw_no_follow(script) self.finished = False @@ -318,7 +322,7 @@ def trace_instance_output(instance_idx, line): sys.stdout.flush() -def run_test_on_instances(test_file, num_instances, instances): +def run_test_on_instances(test_file, num_instances, instances: tuple[TestInstance, ...]): global trace_t0 trace_t0 = time.time() @@ -336,12 +340,12 @@ def run_test_on_instances(test_file, num_instances, instances): injected_globals += "HOST_IP = '" + get_host_ip() + "'\n" if cmd_args.trace_output: - print("TRACE {}:".format("|".join(str(i) for i in instances))) + print("TRACE {}:".format("|".join(str(i.pyinstance) for i in instances))) # Start all instances running, in order, waiting until they signal they are ready for idx in range(num_instances): append_code = APPEND_CODE_TEMPLATE.format(injected_globals, idx) - instance = instances[idx] + instance = instances[idx].pyinstance instance.start_file(test_file, append=append_code) last_read_time = time.time() while True: @@ -382,7 +386,7 @@ def run_test_on_instances(test_file, num_instances, instances): num_running = 0 num_output = 0 for idx in range(num_instances): - instance = instances[idx] + instance = instances[idx].pyinstance if instance.is_finished(): continue num_running += 1 @@ -412,7 +416,7 @@ def run_test_on_instances(test_file, num_instances, instances): last_read_time[idx] = time.time() if out.startswith("BROADCAST "): - for instance2 in instances: + for instance2 in [i.pyinstance for i in instances]: if instance2 is not instance: instance2.write(bytes(out, "ascii") + b"\r\n") elif out.startswith("OUTPUT_METRIC "): @@ -431,7 +435,7 @@ def run_test_on_instances(test_file, num_instances, instances): # Stop all instances for idx in range(num_instances): - instances[idx].stop() + instances[idx].pyinstance.stop() output_str = "" for idx, lines in enumerate(output): @@ -485,11 +489,11 @@ def print_diff(a, b): os.unlink(b_path) -def run_tests(test_files, instances_truth, instances_test): +def run_tests(test_files, instances_truth, instances_test: tuple[TestInstance, ...]): test_results = [] for test_file, num_instances in test_files: - instances_str = "|".join(str(instances_test[i]) for i in range(num_instances)) + instances_str = "|".join(str(instances_test[i].pyinstance) for i in range(num_instances)) print("{} on {}: ".format(test_file, instances_str), end="") if cmd_args.show_output or cmd_args.trace_output: print() @@ -548,6 +552,20 @@ def run_tests(test_files, instances_truth, instances_test): return test_results +@dataclasses.dataclass +class TestInstance: + pyinstance: PyInstance + tw_source: str + "The pathname to a file in tests/target_wiring" + + @property + def target_wiring_specified(self) -> bool: + return self.tw_source != "" + + def __repr__(self): + return f"{self.pyinstance.device}({self.tw_source})" + + def main(): global cmd_args @@ -585,6 +603,12 @@ def main(): default=run_tests_module.base_path("results"), help="directory for test results", ) + cmd_parser.add_argument( + "--target-wiring", + action="append", + default=[], + help="force the given script to be used as target_wiring.py", + ) cmd_parser.add_argument("files", nargs="+", help="input test files") cmd_args = cmd_parser.parse_args() @@ -596,27 +620,50 @@ def main(): instances_truth = [PyInstanceSubProcess([PYTHON_TRUTH]) for _ in range(max_instances)] - instances_test = [] - for i in cmd_args.test_instance: + # Make sure the count matches: test_instance, target_wiring, instances_test + if len(cmd_args.target_wiring) == 0: + cmd_args.target_wiring = [""] * len(cmd_args.test_instance) + + if len(cmd_args.test_instance) != len(cmd_args.target_wiring): + print( + "The argument count of '--target-wiring' must match '--target-instance'", + file=sys.stderr, + ) + sys.exit(2) + + instances_test: list[TestInstance] = [] + for i, w in zip(cmd_args.test_instance, cmd_args.target_wiring, strict=True): # Each instance arg is ,ENV=VAR,ENV=VAR... i = i.split(",") cmd = i[0] env = i[1:] if cmd.startswith("exec:"): - instances_test.append(PyInstanceSubProcess([cmd[len("exec:") :]], env)) + pyinstance = PyInstanceSubProcess([cmd[len("exec:") :]], env) elif cmd == "unix": - instances_test.append(PyInstanceSubProcess([MICROPYTHON], env)) + pyinstance = PyInstanceSubProcess([MICROPYTHON], env) elif cmd == "cpython": - instances_test.append(PyInstanceSubProcess([CPYTHON3], env)) + pyinstance = PyInstanceSubProcess([CPYTHON3], env) elif cmd == "webassembly" or cmd.startswith("execpty:"): print("unsupported instance string: {}".format(cmd), file=sys.stderr) sys.exit(2) else: device = run_tests_module.convert_device_shortcut_to_real_device(cmd) - instances_test.append(PyInstancePyboard(device)) + pyinstance = PyInstancePyboard(device) + + instances_test.append(TestInstance(pyinstance=pyinstance, tw_source=w)) for _ in range(max_instances - len(instances_test)): - instances_test.append(PyInstanceSubProcess([MICROPYTHON])) + instances_test.append( + TestInstance(pyinstance=PyInstanceSubProcess([MICROPYTHON]), tw_source="") + ) + + for instance in instances_test: + run_tests_module.detect_target_wiring_script2( + pyb=instance.pyinstance, + target_wiring=instance.tw_source, + platform=None, # TODO: correct parameter + build="", # TODO: correct parameter + ) os.makedirs(cmd_args.result_dir, exist_ok=True) all_pass = True @@ -632,7 +679,7 @@ def main(): for i in instances_truth: i.close() for i in instances_test: - i.close() + i.pyinstance.close() if not all_pass: sys.exit(1) diff --git a/tests/run-tests.py b/tests/run-tests.py index 3c324a2906ac2..c92dae05ee6c3 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -359,19 +359,27 @@ def detect_test_platform(pyb, args): def detect_target_wiring_script(pyb, args): + # TODO: How to detect 'platform' and 'build' + detect_target_wiring_script2( + pyb, target_wiring=args.target_wiring, platform=args.platform, build=args.build + ) + + +def detect_target_wiring_script2(pyb, target_wiring, platform, build): tw_data = b"" tw_source = None - if args.target_wiring: + if target_wiring: # A target_wiring path is explicitly provided, so use that. - tw_source = args.target_wiring + tw_source = target_wiring with open(tw_source, "rb") as f: tw_data = f.read() + elif hasattr(pyb, "exec_raw") and pyb.exec_raw("import target_wiring") == (b"", b""): # The board already has a target_wiring module available, so use that. tw_source = "on-device" else: - port = platform_to_port(args.platform) - build = args.build + port = platform_to_port(platform) + build = build tw_board_exact = None tw_board_partial = None tw_port = None @@ -1232,6 +1240,7 @@ def to_json(obj): }, f, default=to_json, + indent=2, ) # Return True only if all tests succeeded. diff --git a/tests/target_wiring/rp2.py b/tests/target_wiring/rp2.py index cb0fa0d626339..b651e7a1b721f 100644 --- a/tests/target_wiring/rp2.py +++ b/tests/target_wiring/rp2.py @@ -5,3 +5,7 @@ uart_loopback_args = (0,) uart_loopback_kwargs = {"tx": "GPIO0", "rx": "GPIO1"} + +i2c_args = (0,) +i2c_kwargs = {"scl": 9, "sda": 8} + diff --git a/tests/target_wiring/rp2_octoprobe_infra.py b/tests/target_wiring/rp2_octoprobe_infra.py new file mode 100644 index 0000000000000..3cdfd7bdb03be --- /dev/null +++ b/tests/target_wiring/rp2_octoprobe_infra.py @@ -0,0 +1,5 @@ +# Target wiring for rp2 on octoprobe tentacle. +# + +i2c_args = (1,) +i2c_kwargs = {"scl": 11, "sda": 10}