summaryrefslogtreecommitdiffstatshomepage
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/basics/array_add.py6
-rw-r--r--tests/basics/io_buffered_writer.py24
-rw-r--r--tests/basics/io_buffered_writer.py.exp5
-rw-r--r--tests/basics/string_format.py10
-rw-r--r--tests/cmdline/repl_autocomplete_underscore.py33
-rw-r--r--tests/cmdline/repl_autocomplete_underscore.py.exp41
-rw-r--r--tests/cmdline/repl_paste.py90
-rw-r--r--tests/cmdline/repl_paste.py.exp133
-rw-r--r--tests/cpydiff/core_fstring_concat.py2
-rw-r--r--tests/cpydiff/core_fstring_parser.py2
-rw-r--r--tests/cpydiff/core_fstring_repr.py2
-rw-r--r--tests/cpydiff/core_import_all.py10
-rw-r--r--tests/cpydiff/modules3/__init__.py1
-rw-r--r--tests/cpydiff/modules3/foo.py2
-rw-r--r--tests/cpydiff/syntax_arg_unpacking.py2
-rw-r--r--tests/cpydiff/syntax_literal_underscore.py19
-rw-r--r--tests/cpydiff/syntax_spaces.py19
-rw-r--r--tests/cpydiff/types_complex_parser.py14
-rw-r--r--tests/cpydiff/types_str_formatsep.py19
-rw-r--r--tests/extmod/framebuf_blit.py68
-rw-r--r--tests/extmod/framebuf_blit.py.exp45
-rw-r--r--tests/extmod/json_loads.py24
-rw-r--r--tests/extmod/platform_basic.py8
-rw-r--r--tests/extmod/random_extra_float.py8
-rw-r--r--tests/extmod/vfs_lfs_error.py46
-rw-r--r--tests/extmod/vfs_lfs_error.py.exp44
-rw-r--r--tests/extmod/vfs_rom.py1
-rw-r--r--tests/float/complex1.py2
-rw-r--r--tests/float/float_array.py8
-rw-r--r--tests/float/math_constants.py27
-rw-r--r--tests/float/math_constants_extra.py3
-rw-r--r--tests/import/import_star.py59
-rw-r--r--tests/import/pkgstar_all_array/__init__.py49
-rw-r--r--tests/import/pkgstar_all_array/funcs.py2
-rw-r--r--tests/import/pkgstar_all_inval/__init__.py1
-rw-r--r--tests/import/pkgstar_all_miss/__init__.py8
-rw-r--r--tests/import/pkgstar_all_tuple/__init__.py22
-rw-r--r--tests/import/pkgstar_default/__init__.py20
-rw-r--r--tests/inlineasm/xtensa/asmargs.py44
-rw-r--r--tests/inlineasm/xtensa/asmargs.py.exp5
-rw-r--r--tests/inlineasm/xtensa/asmarith.py119
-rw-r--r--tests/inlineasm/xtensa/asmarith.py.exp40
-rw-r--r--tests/inlineasm/xtensa/asmbranch.py299
-rw-r--r--tests/inlineasm/xtensa/asmbranch.py.exp66
-rw-r--r--tests/inlineasm/xtensa/asmjump.py26
-rw-r--r--tests/inlineasm/xtensa/asmjump.py.exp2
-rw-r--r--tests/inlineasm/xtensa/asmloadstore.py98
-rw-r--r--tests/inlineasm/xtensa/asmloadstore.py.exp9
-rw-r--r--tests/inlineasm/xtensa/asmmisc.py25
-rw-r--r--tests/inlineasm/xtensa/asmmisc.py.exp3
-rw-r--r--tests/inlineasm/xtensa/asmshift.py137
-rw-r--r--tests/inlineasm/xtensa/asmshift.py.exp17
-rw-r--r--tests/micropython/viper_ptr16_load_boundary.py25
-rw-r--r--tests/micropython/viper_ptr16_load_boundary.py.exp12
-rw-r--r--tests/micropython/viper_ptr16_store_boundary.py41
-rw-r--r--tests/micropython/viper_ptr16_store_boundary.py.exp18
-rw-r--r--tests/micropython/viper_ptr32_load_boundary.py25
-rw-r--r--tests/micropython/viper_ptr32_load_boundary.py.exp12
-rw-r--r--tests/micropython/viper_ptr32_store_boundary.py35
-rw-r--r--tests/micropython/viper_ptr32_store_boundary.py.exp18
-rw-r--r--tests/micropython/viper_ptr8_load_boundary.py25
-rw-r--r--tests/micropython/viper_ptr8_load_boundary.py.exp12
-rw-r--r--tests/micropython/viper_ptr8_store_boundary.py30
-rw-r--r--tests/micropython/viper_ptr8_store_boundary.py.exp12
-rw-r--r--tests/misc/print_exception.py2
-rw-r--r--tests/multi_net/tcp_accept_recv.py73
-rw-r--r--tests/multi_net/tcp_recv_peek.py46
-rw-r--r--tests/multi_net/udp_data_multi.py69
-rw-r--r--tests/multi_net/udp_data_multi.py.exp15
-rw-r--r--tests/multi_net/udp_recv_dontwait.py59
-rw-r--r--tests/multi_net/udp_recv_dontwait.py.exp7
-rw-r--r--tests/multi_net/udp_recv_peek.py36
-rw-r--r--tests/net_inet/mpycert.derbin1290 -> 1289 bytes
-rw-r--r--tests/net_inet/ssl_cert.py79
-rw-r--r--tests/net_inet/test_sslcontext_client.py10
-rw-r--r--tests/ports/rp2/rp2_lightsleep_thread.py67
-rw-r--r--tests/ports/rp2/rp2_machine_idle.py6
-rw-r--r--tests/ports/rp2/rp2_machine_timer.py20
-rw-r--r--tests/ports/rp2/rp2_machine_timer.py.exp16
-rw-r--r--tests/ports/unix/extra_coverage.py.exp32
-rwxr-xr-xtests/run-multitests.py32
-rwxr-xr-xtests/run-natmodtests.py52
-rwxr-xr-xtests/run-perfbench.py29
-rwxr-xr-xtests/run-tests.py162
84 files changed, 2594 insertions, 252 deletions
diff --git a/tests/basics/array_add.py b/tests/basics/array_add.py
index 76ce59f761..e78615541c 100644
--- a/tests/basics/array_add.py
+++ b/tests/basics/array_add.py
@@ -14,3 +14,9 @@ print(a1)
a1.extend(array.array('I', [5]))
print(a1)
+
+a1.extend([6, 7])
+print(a1)
+
+a1.extend(i for i in (8, 9))
+print(a1)
diff --git a/tests/basics/io_buffered_writer.py b/tests/basics/io_buffered_writer.py
index 5c065f158a..60cf2c837d 100644
--- a/tests/basics/io_buffered_writer.py
+++ b/tests/basics/io_buffered_writer.py
@@ -28,3 +28,27 @@ print(bts.getvalue())
# hashing a BufferedWriter
print(type(hash(buf)))
+
+# Test failing flush()
+class MyIO(io.IOBase):
+ def __init__(self):
+ self.count = 0
+
+ def write(self, buf):
+ self.count += 1
+ if self.count < 3:
+ return None
+ print("writing", buf)
+ return len(buf)
+
+
+buf = io.BufferedWriter(MyIO(), 8)
+
+buf.write(b"foobar")
+
+for _ in range(4):
+ try:
+ buf.flush()
+ print("flushed")
+ except OSError:
+ print("OSError")
diff --git a/tests/basics/io_buffered_writer.py.exp b/tests/basics/io_buffered_writer.py.exp
index 2209348f5a..d61eb148b4 100644
--- a/tests/basics/io_buffered_writer.py.exp
+++ b/tests/basics/io_buffered_writer.py.exp
@@ -4,3 +4,8 @@ b'foobarfoobar'
b'foobarfoobar'
b'foo'
<class 'int'>
+OSError
+OSError
+writing bytearray(b'foobar')
+flushed
+flushed
diff --git a/tests/basics/string_format.py b/tests/basics/string_format.py
index e8600f5836..11e7836a73 100644
--- a/tests/basics/string_format.py
+++ b/tests/basics/string_format.py
@@ -22,7 +22,17 @@ test("{:4o}", 123)
test("{:4x}", 123)
test("{:4X}", 123)
+test("{:4,d}", 1)
+test("{:4_d}", 1)
+test("{:4_o}", 1)
+test("{:4_b}", 1)
+test("{:4_x}", 1)
+
test("{:4,d}", 12345678)
+test("{:4_d}", 12345678)
+test("{:4_o}", 12345678)
+test("{:4_b}", 12345678)
+test("{:4_x}", 12345678)
test("{:#4b}", 10)
test("{:#4o}", 123)
diff --git a/tests/cmdline/repl_autocomplete_underscore.py b/tests/cmdline/repl_autocomplete_underscore.py
new file mode 100644
index 0000000000..98bbb69920
--- /dev/null
+++ b/tests/cmdline/repl_autocomplete_underscore.py
@@ -0,0 +1,33 @@
+# Test REPL autocompletion filtering of underscore attributes
+
+# Start paste mode
+{\x05}
+class TestClass:
+ def __init__(self):
+ self.public_attr = 1
+ self._private_attr = 2
+ self.__very_private = 3
+
+ def public_method(self):
+ pass
+
+ def _private_method(self):
+ pass
+
+ @property
+ def public_property(self):
+ return 42
+
+ @property
+ def _private_property(self):
+ return 99
+
+{\x04}
+# Paste executed
+
+# Create an instance
+obj = TestClass()
+
+# Test tab completion on the instance
+# The tab character after `obj.` and 'a' below triggers the completions
+obj.{\x09}{\x09}a{\x09}
diff --git a/tests/cmdline/repl_autocomplete_underscore.py.exp b/tests/cmdline/repl_autocomplete_underscore.py.exp
new file mode 100644
index 0000000000..35617554f5
--- /dev/null
+++ b/tests/cmdline/repl_autocomplete_underscore.py.exp
@@ -0,0 +1,41 @@
+MicroPython \.\+ version
+Use Ctrl-D to exit, Ctrl-E for paste mode
+>>> # Test REPL autocompletion filtering of underscore attributes
+>>>
+>>> # Start paste mode
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== class TestClass:
+=== def __init__(self):
+=== self.public_attr = 1
+=== self._private_attr = 2
+=== self.__very_private = 3
+===
+=== def public_method(self):
+=== pass
+===
+=== def _private_method(self):
+=== pass
+===
+=== @property
+=== def public_property(self):
+=== return 42
+===
+=== @property
+=== def _private_property(self):
+=== return 99
+===
+===
+>>> # Paste executed
+>>>
+>>> # Create an instance
+>>> obj = TestClass()
+>>>
+>>> # Test tab completion on the instance
+>>> # The tab character after `obj.` and 'a' below triggers the completions
+>>> obj.public_
+public_attr public_method public_property
+>>> obj.public_attr
+1
+>>>
diff --git a/tests/cmdline/repl_paste.py b/tests/cmdline/repl_paste.py
new file mode 100644
index 0000000000..7cec450fce
--- /dev/null
+++ b/tests/cmdline/repl_paste.py
@@ -0,0 +1,90 @@
+# Test REPL paste mode functionality
+
+# Basic paste mode with a simple function
+{\x05}
+def hello():
+ print('Hello from paste mode!')
+hello()
+{\x04}
+
+# Paste mode with multiple indentation levels
+{\x05}
+def calculate(n):
+ if n > 0:
+ for i in range(n):
+ if i % 2 == 0:
+ print(f'Even: {i}')
+ else:
+ print(f'Odd: {i}')
+ else:
+ print('n must be positive')
+
+calculate(5)
+{\x04}
+
+# Paste mode with blank lines
+{\x05}
+def function_with_blanks():
+ print('First line')
+
+ print('After blank line')
+
+
+ print('After two blank lines')
+
+function_with_blanks()
+{\x04}
+
+# Paste mode with class definition and multiple methods
+{\x05}
+class TestClass:
+ def __init__(self, value):
+ self.value = value
+
+ def display(self):
+ print(f'Value is: {self.value}')
+
+ def double(self):
+ self.value *= 2
+ return self.value
+
+obj = TestClass(21)
+obj.display()
+print(f'Doubled: {obj.double()}')
+obj.display()
+{\x04}
+
+# Paste mode with exception handling
+{\x05}
+try:
+ x = 1 / 0
+except ZeroDivisionError:
+ print('Caught division by zero')
+finally:
+ print('Finally block executed')
+{\x04}
+
+# Cancel paste mode with Ctrl-C
+{\x05}
+print('This should not execute')
+{\x03}
+
+# Normal REPL still works after cancelled paste
+print('Back to normal REPL')
+
+# Paste mode with syntax error
+{\x05}
+def bad_syntax(:
+ print('Missing parameter')
+{\x04}
+
+# Paste mode with runtime error
+{\x05}
+def will_error():
+ undefined_variable
+
+will_error()
+{\x04}
+
+# Final test to show REPL is still functioning
+1 + 2 + 3
diff --git a/tests/cmdline/repl_paste.py.exp b/tests/cmdline/repl_paste.py.exp
new file mode 100644
index 0000000000..22d9bd5740
--- /dev/null
+++ b/tests/cmdline/repl_paste.py.exp
@@ -0,0 +1,133 @@
+MicroPython \.\+ version
+Use Ctrl-D to exit, Ctrl-E for paste mode
+>>> # Test REPL paste mode functionality
+>>>
+>>> # Basic paste mode with a simple function
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== def hello():
+=== print('Hello from paste mode!')
+=== hello()
+===
+Hello from paste mode!
+>>>
+>>> # Paste mode with multiple indentation levels
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== def calculate(n):
+=== if n > 0:
+=== for i in range(n):
+=== if i % 2 == 0:
+=== print(f'Even: {i}')
+=== else:
+=== print(f'Odd: {i}')
+=== else:
+=== print('n must be positive')
+===
+=== calculate(5)
+===
+Even: 0
+Odd: 1
+Even: 2
+Odd: 3
+Even: 4
+>>>
+>>> # Paste mode with blank lines
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== def function_with_blanks():
+=== print('First line')
+===
+=== print('After blank line')
+===
+===
+=== print('After two blank lines')
+===
+=== function_with_blanks()
+===
+First line
+After blank line
+After two blank lines
+>>>
+>>> # Paste mode with class definition and multiple methods
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== class TestClass:
+=== def __init__(self, value):
+=== self.value = value
+===
+=== def display(self):
+=== print(f'Value is: {self.value}')
+===
+=== def double(self):
+=== self.value *= 2
+=== return self.value
+===
+=== obj = TestClass(21)
+=== obj.display()
+=== print(f'Doubled: {obj.double()}')
+=== obj.display()
+===
+Value is: 21
+Doubled: 42
+Value is: 42
+>>>
+>>> # Paste mode with exception handling
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== try:
+=== x = 1 / 0
+=== except ZeroDivisionError:
+=== print('Caught division by zero')
+=== finally:
+=== print('Finally block executed')
+===
+Caught division by zero
+Finally block executed
+>>>
+>>> # Cancel paste mode with Ctrl-C
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== print('This should not execute')
+===
+>>>
+>>>
+>>> # Normal REPL still works after cancelled paste
+>>> print('Back to normal REPL')
+Back to normal REPL
+>>>
+>>> # Paste mode with syntax error
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== def bad_syntax(:
+=== print('Missing parameter')
+===
+Traceback (most recent call last):
+ File "<stdin>", line 2
+SyntaxError: invalid syntax
+>>>
+>>> # Paste mode with runtime error
+>>>
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+===
+=== def will_error():
+=== undefined_variable
+===
+=== will_error()
+===
+Traceback (most recent call last):
+ File "<stdin>", line 5, in <module>
+ File "<stdin>", line 3, in will_error
+NameError: name 'undefined_variable' isn't defined
+>>>
+>>> # Final test to show REPL is still functioning
+>>> 1 + 2 + 3
+6
+>>>
diff --git a/tests/cpydiff/core_fstring_concat.py b/tests/cpydiff/core_fstring_concat.py
index 3daa13d753..2fbe1b961a 100644
--- a/tests/cpydiff/core_fstring_concat.py
+++ b/tests/cpydiff/core_fstring_concat.py
@@ -1,5 +1,5 @@
"""
-categories: Core
+categories: Core,f-strings
description: f-strings don't support concatenation with adjacent literals if the adjacent literals contain braces
cause: MicroPython is optimised for code space.
workaround: Use the + operator between literal strings when they are not both f-strings
diff --git a/tests/cpydiff/core_fstring_parser.py b/tests/cpydiff/core_fstring_parser.py
index 22bbc5866e..570b92434a 100644
--- a/tests/cpydiff/core_fstring_parser.py
+++ b/tests/cpydiff/core_fstring_parser.py
@@ -1,5 +1,5 @@
"""
-categories: Core
+categories: Core,f-strings
description: f-strings cannot support expressions that require parsing to resolve unbalanced nested braces and brackets
cause: MicroPython is optimised for code space.
workaround: Always use balanced braces and brackets in expressions inside f-strings
diff --git a/tests/cpydiff/core_fstring_repr.py b/tests/cpydiff/core_fstring_repr.py
index d37fb48db7..2589a34b7e 100644
--- a/tests/cpydiff/core_fstring_repr.py
+++ b/tests/cpydiff/core_fstring_repr.py
@@ -1,5 +1,5 @@
"""
-categories: Core
+categories: Core,f-strings
description: f-strings don't support !a conversions
cause: MicropPython does not implement ascii()
workaround: None
diff --git a/tests/cpydiff/core_import_all.py b/tests/cpydiff/core_import_all.py
deleted file mode 100644
index 0fbe9d4d4e..0000000000
--- a/tests/cpydiff/core_import_all.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-categories: Core,import
-description: __all__ is unsupported in __init__.py in MicroPython.
-cause: Not implemented.
-workaround: Manually import the sub-modules directly in __init__.py using ``from . import foo, bar``.
-"""
-
-from modules3 import *
-
-foo.hello()
diff --git a/tests/cpydiff/modules3/__init__.py b/tests/cpydiff/modules3/__init__.py
deleted file mode 100644
index 27a2bf2ad9..0000000000
--- a/tests/cpydiff/modules3/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__all__ = ["foo"]
diff --git a/tests/cpydiff/modules3/foo.py b/tests/cpydiff/modules3/foo.py
deleted file mode 100644
index dd9b9d4ddd..0000000000
--- a/tests/cpydiff/modules3/foo.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def hello():
- print("hello")
diff --git a/tests/cpydiff/syntax_arg_unpacking.py b/tests/cpydiff/syntax_arg_unpacking.py
index e54832ddb9..7133a8a282 100644
--- a/tests/cpydiff/syntax_arg_unpacking.py
+++ b/tests/cpydiff/syntax_arg_unpacking.py
@@ -1,5 +1,5 @@
"""
-categories: Syntax
+categories: Syntax,Unpacking
description: Argument unpacking does not work if the argument being unpacked is the nth or greater argument where n is the number of bits in an MP_SMALL_INT.
cause: The implementation uses an MP_SMALL_INT to flag args that need to be unpacked.
workaround: Use fewer arguments.
diff --git a/tests/cpydiff/syntax_literal_underscore.py b/tests/cpydiff/syntax_literal_underscore.py
new file mode 100644
index 0000000000..4b1406e9f3
--- /dev/null
+++ b/tests/cpydiff/syntax_literal_underscore.py
@@ -0,0 +1,19 @@
+"""
+categories: Syntax,Literals
+description: MicroPython accepts underscores in numeric literals where CPython doesn't
+cause: Different parser implementation
+
+MicroPython's tokenizer ignores underscores in numeric literals, while CPython
+rejects multiple consecutive underscores and underscores after the last digit.
+
+workaround: Remove the underscores not accepted by CPython.
+"""
+
+try:
+ print(eval("1__1"))
+except SyntaxError:
+ print("Should not work")
+try:
+ print(eval("1_"))
+except SyntaxError:
+ print("Should not work")
diff --git a/tests/cpydiff/syntax_spaces.py b/tests/cpydiff/syntax_spaces.py
index 03d25d5619..670cefdeac 100644
--- a/tests/cpydiff/syntax_spaces.py
+++ b/tests/cpydiff/syntax_spaces.py
@@ -1,8 +1,15 @@
"""
-categories: Syntax,Spaces
-description: uPy requires spaces between literal numbers and keywords, CPy doesn't
-cause: Unknown
-workaround: Unknown
+categories: Syntax,Literals
+description: MicroPython requires spaces between literal numbers and keywords or ".", CPython doesn't
+cause: Different parser implementation
+
+MicroPython's tokenizer treats a sequence like ``1and`` as a single token, while CPython treats it as two tokens.
+
+Since CPython 3.11, when the literal number is followed by a token, this syntax causes a ``SyntaxWarning`` for an "invalid literal". When a literal number is followed by a "." denoting attribute access, CPython does not warn.
+
+workaround: Add a space between the integer literal and the intended next token.
+
+This also fixes the ``SyntaxWarning`` in CPython.
"""
try:
@@ -17,3 +24,7 @@ try:
print(eval("1if 1else 0"))
except SyntaxError:
print("Should have worked")
+try:
+ print(eval("0x1.to_bytes(1)"))
+except SyntaxError:
+ print("Should have worked")
diff --git a/tests/cpydiff/types_complex_parser.py b/tests/cpydiff/types_complex_parser.py
new file mode 100644
index 0000000000..4a012987d9
--- /dev/null
+++ b/tests/cpydiff/types_complex_parser.py
@@ -0,0 +1,14 @@
+"""
+categories: Types,complex
+description: MicroPython's complex() accepts certain incorrect values that CPython rejects
+cause: MicroPython is highly optimized for memory usage.
+workaround: Do not use non-standard complex literals as argument to complex()
+
+MicroPython's ``complex()`` function accepts literals that contain a space and
+no sign between the real and imaginary parts, and interprets it as a plus.
+"""
+
+try:
+ print(complex("1 1j"))
+except ValueError:
+ print("ValueError")
diff --git a/tests/cpydiff/types_str_formatsep.py b/tests/cpydiff/types_str_formatsep.py
new file mode 100644
index 0000000000..05d0b8d3d2
--- /dev/null
+++ b/tests/cpydiff/types_str_formatsep.py
@@ -0,0 +1,19 @@
+"""
+categories: Types,str
+description: MicroPython accepts the "," grouping option with any radix, unlike CPython
+cause: To reduce code size, MicroPython does not issue an error for this combination
+workaround: Do not use a format string like ``{:,b}`` if CPython compatibility is required.
+"""
+
+try:
+ print("{:,b}".format(99))
+except ValueError:
+ print("ValueError")
+try:
+ print("{:,x}".format(99))
+except ValueError:
+ print("ValueError")
+try:
+ print("{:,o}".format(99))
+except ValueError:
+ print("ValueError")
diff --git a/tests/extmod/framebuf_blit.py b/tests/extmod/framebuf_blit.py
new file mode 100644
index 0000000000..b1d98b330a
--- /dev/null
+++ b/tests/extmod/framebuf_blit.py
@@ -0,0 +1,68 @@
+# Test FrameBuffer.blit method.
+
+try:
+ import framebuf
+except ImportError:
+ print("SKIP")
+ raise SystemExit
+
+
+def printbuf():
+ print("--8<--")
+ for y in range(h):
+ for x in range(w):
+ print("%02x" % buf[(x + y * w)], end="")
+ print()
+ print("-->8--")
+
+
+w = 5
+h = 4
+buf = bytearray(w * h)
+fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8)
+
+fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8)
+fbuf2.fill(0xFF)
+
+# Blit another FrameBuffer, at various locations.
+for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)):
+ fbuf.fill(0)
+ fbuf.blit(fbuf2, x, y)
+ printbuf()
+
+# Blit a bytes object.
+fbuf.fill(0)
+image = (b"\x10\x11\x12\x13", 2, 2, framebuf.GS8)
+fbuf.blit(image, 1, 1)
+printbuf()
+
+# Blit a bytes object that has a stride.
+fbuf.fill(0)
+image = (b"\x20\x21\xff\x22\x23\xff", 2, 2, framebuf.GS8, 3)
+fbuf.blit(image, 1, 1)
+printbuf()
+
+# Blit a bytes object with a bytes palette.
+fbuf.fill(0)
+image = (b"\x00\x01\x01\x00", 2, 2, framebuf.GS8)
+palette = (b"\xa1\xa2", 2, 1, framebuf.GS8)
+fbuf.blit(image, 1, 1, -1, palette)
+printbuf()
+
+# Not enough elements in the tuple.
+try:
+ fbuf.blit((0, 0, 0), 0, 0)
+except ValueError:
+ print("ValueError")
+
+# Too many elements in the tuple.
+try:
+ fbuf.blit((0, 0, 0, 0, 0, 0), 0, 0)
+except ValueError:
+ print("ValueError")
+
+# Bytes too small.
+try:
+ fbuf.blit((b"", 1, 1, framebuf.GS8), 0, 0)
+except ValueError:
+ print("ValueError")
diff --git a/tests/extmod/framebuf_blit.py.exp b/tests/extmod/framebuf_blit.py.exp
new file mode 100644
index 0000000000..e340f1990c
--- /dev/null
+++ b/tests/extmod/framebuf_blit.py.exp
@@ -0,0 +1,45 @@
+--8<--
+ff00000000
+0000000000
+0000000000
+0000000000
+-->8--
+--8<--
+ffff000000
+ffff000000
+0000000000
+0000000000
+-->8--
+--8<--
+0000000000
+00ffff0000
+00ffff0000
+0000000000
+-->8--
+--8<--
+0000000000
+0000000000
+0000000000
+00000000ff
+-->8--
+--8<--
+0000000000
+0010110000
+0012130000
+0000000000
+-->8--
+--8<--
+0000000000
+0020210000
+0022230000
+0000000000
+-->8--
+--8<--
+0000000000
+00a1a20000
+00a2a10000
+0000000000
+-->8--
+ValueError
+ValueError
+ValueError
diff --git a/tests/extmod/json_loads.py b/tests/extmod/json_loads.py
index f9073c121e..095e67d740 100644
--- a/tests/extmod/json_loads.py
+++ b/tests/extmod/json_loads.py
@@ -71,3 +71,27 @@ try:
my_print(json.loads("[null] a"))
except ValueError:
print("ValueError")
+
+# incomplete object declaration
+try:
+ my_print(json.loads('{"a":0,'))
+except ValueError:
+ print("ValueError")
+
+# incomplete nested array declaration
+try:
+ my_print(json.loads('{"a":0, ['))
+except ValueError:
+ print("ValueError")
+
+# incomplete array declaration
+try:
+ my_print(json.loads('[0,'))
+except ValueError:
+ print("ValueError")
+
+# incomplete nested object declaration
+try:
+ my_print(json.loads('[0, {"a":0, '))
+except ValueError:
+ print("ValueError")
diff --git a/tests/extmod/platform_basic.py b/tests/extmod/platform_basic.py
new file mode 100644
index 0000000000..eb6f2be13c
--- /dev/null
+++ b/tests/extmod/platform_basic.py
@@ -0,0 +1,8 @@
+try:
+ import platform
+except ImportError:
+ print("SKIP")
+ raise SystemExit
+
+print(type(platform.python_compiler()))
+print(type(platform.libc_ver()))
diff --git a/tests/extmod/random_extra_float.py b/tests/extmod/random_extra_float.py
index 3b37ed8dce..03973c5834 100644
--- a/tests/extmod/random_extra_float.py
+++ b/tests/extmod/random_extra_float.py
@@ -1,12 +1,8 @@
try:
import random
-except ImportError:
- print("SKIP")
- raise SystemExit
-try:
- random.randint
-except AttributeError:
+ random.random
+except (ImportError, AttributeError):
print("SKIP")
raise SystemExit
diff --git a/tests/extmod/vfs_lfs_error.py b/tests/extmod/vfs_lfs_error.py
index 2ac7629bfa..73cdf34373 100644
--- a/tests/extmod/vfs_lfs_error.py
+++ b/tests/extmod/vfs_lfs_error.py
@@ -1,7 +1,7 @@
# Test for VfsLittle using a RAM device, testing error handling
try:
- import vfs
+ import errno, vfs
vfs.VfsLfs1
vfs.VfsLfs2
@@ -41,14 +41,14 @@ def test(bdev, vfs_class):
# mkfs with too-small block device
try:
vfs_class.mkfs(RAMBlockDevice(1))
- except OSError:
- print("mkfs OSError")
+ except OSError as er:
+ print("mkfs OSError", er.errno > 0)
# mount with invalid filesystem
try:
vfs_class(bdev)
- except OSError:
- print("mount OSError")
+ except OSError as er:
+ print("mount OSError", er.errno > 0)
# set up for following tests
vfs_class.mkfs(bdev)
@@ -60,60 +60,60 @@ def test(bdev, vfs_class):
# ilistdir
try:
fs.ilistdir("noexist")
- except OSError:
- print("ilistdir OSError")
+ except OSError as er:
+ print("ilistdir OSError", er)
# remove
try:
fs.remove("noexist")
- except OSError:
- print("remove OSError")
+ except OSError as er:
+ print("remove OSError", er)
# rmdir
try:
fs.rmdir("noexist")
- except OSError:
- print("rmdir OSError")
+ except OSError as er:
+ print("rmdir OSError", er)
# rename
try:
fs.rename("noexist", "somethingelse")
- except OSError:
- print("rename OSError")
+ except OSError as er:
+ print("rename OSError", er)
# mkdir
try:
fs.mkdir("testdir")
- except OSError:
- print("mkdir OSError")
+ except OSError as er:
+ print("mkdir OSError", er)
# chdir to nonexistent
try:
fs.chdir("noexist")
- except OSError:
- print("chdir OSError")
+ except OSError as er:
+ print("chdir OSError", er)
print(fs.getcwd()) # check still at root
# chdir to file
try:
fs.chdir("testfile")
- except OSError:
- print("chdir OSError")
+ except OSError as er:
+ print("chdir OSError", er)
print(fs.getcwd()) # check still at root
# stat
try:
fs.stat("noexist")
- except OSError:
- print("stat OSError")
+ except OSError as er:
+ print("stat OSError", er)
# error during seek
with fs.open("testfile", "r") as f:
f.seek(1 << 30) # SEEK_SET
try:
f.seek(1 << 30, 1) # SEEK_CUR
- except OSError:
- print("seek OSError")
+ except OSError as er:
+ print("seek OSError", er)
bdev = RAMBlockDevice(30)
diff --git a/tests/extmod/vfs_lfs_error.py.exp b/tests/extmod/vfs_lfs_error.py.exp
index f4327f6962..440607ed84 100644
--- a/tests/extmod/vfs_lfs_error.py.exp
+++ b/tests/extmod/vfs_lfs_error.py.exp
@@ -1,28 +1,28 @@
test <class 'VfsLfs1'>
-mkfs OSError
-mount OSError
-ilistdir OSError
-remove OSError
-rmdir OSError
-rename OSError
-mkdir OSError
-chdir OSError
+mkfs OSError True
+mount OSError True
+ilistdir OSError [Errno 2] ENOENT
+remove OSError [Errno 2] ENOENT
+rmdir OSError [Errno 2] ENOENT
+rename OSError [Errno 2] ENOENT
+mkdir OSError [Errno 17] EEXIST
+chdir OSError [Errno 2] ENOENT
/
-chdir OSError
+chdir OSError [Errno 2] ENOENT
/
-stat OSError
-seek OSError
+stat OSError [Errno 2] ENOENT
+seek OSError [Errno 22] EINVAL
test <class 'VfsLfs2'>
-mkfs OSError
-mount OSError
-ilistdir OSError
-remove OSError
-rmdir OSError
-rename OSError
-mkdir OSError
-chdir OSError
+mkfs OSError True
+mount OSError True
+ilistdir OSError [Errno 2] ENOENT
+remove OSError [Errno 2] ENOENT
+rmdir OSError [Errno 2] ENOENT
+rename OSError [Errno 2] ENOENT
+mkdir OSError [Errno 17] EEXIST
+chdir OSError [Errno 2] ENOENT
/
-chdir OSError
+chdir OSError [Errno 2] ENOENT
/
-stat OSError
-seek OSError
+stat OSError [Errno 2] ENOENT
+seek OSError [Errno 22] EINVAL
diff --git a/tests/extmod/vfs_rom.py b/tests/extmod/vfs_rom.py
index 770b6863b9..cd14542ea6 100644
--- a/tests/extmod/vfs_rom.py
+++ b/tests/extmod/vfs_rom.py
@@ -394,6 +394,7 @@ class TestMounted(TestBase):
def setUp(self):
self.orig_sys_path = list(sys.path)
self.orig_cwd = os.getcwd()
+ sys.path = []
vfs.mount(vfs.VfsRom(self.romfs), "/test_rom")
def tearDown(self):
diff --git a/tests/float/complex1.py b/tests/float/complex1.py
index f4107a1390..0a1d98b9af 100644
--- a/tests/float/complex1.py
+++ b/tests/float/complex1.py
@@ -12,9 +12,11 @@ print(complex("1.2j"))
print(complex("1+j"))
print(complex("1+2j"))
print(complex("-1-2j"))
+print(complex("-1+2j"))
print(complex("+1-2j"))
print(complex(" -1-2j "))
print(complex(" +1-2j "))
+print(complex(" -1+2j "))
print(complex("nanj"))
print(complex("nan-infj"))
print(complex(1, 2))
diff --git a/tests/float/float_array.py b/tests/float/float_array.py
index 3d128da838..cfff3b220c 100644
--- a/tests/float/float_array.py
+++ b/tests/float/float_array.py
@@ -19,4 +19,10 @@ def test(a):
test(array("f"))
test(array("d"))
-print("{:.4f}".format(array("f", bytes(array("I", [0x3DCCCCCC])))[0]))
+# hand-crafted floats, including non-standard nan
+for float_hex in (0x3DCCCCCC, 0x7F800024, 0x7FC00004):
+ f = array("f", bytes(array("I", [float_hex])))[0]
+ if type(f) is float:
+ print("{:.4e}".format(f))
+ else:
+ print(f)
diff --git a/tests/float/math_constants.py b/tests/float/math_constants.py
index 2e4c321052..21d822a01e 100644
--- a/tests/float/math_constants.py
+++ b/tests/float/math_constants.py
@@ -1,11 +1,30 @@
# Tests various constants of the math module.
+
+import sys
+
try:
- import math
- from math import exp, cos
+ from array import array
+ from math import e, pi
except ImportError:
print("SKIP")
raise SystemExit
-print(math.e == exp(1.0))
+# Hexadecimal representations of e and pi constants.
+e_truth_single = 0x402DF854
+pi_truth_single = 0x40490FDB
+e_truth_double = 0x4005BF0A8B145769
+pi_truth_double = 0x400921FB54442D18
+
+# Detect the floating-point precision of the system, to determine the exact values of
+# the constants (parsing the float from a decimal string can lead to inaccuracies).
+if float("1e300") == float("inf"):
+ # Single precision floats.
+ e_truth = array("f", e_truth_single.to_bytes(4, sys.byteorder))[0]
+ pi_truth = array("f", pi_truth_single.to_bytes(4, sys.byteorder))[0]
+else:
+ # Double precision floats.
+ e_truth = array("d", e_truth_double.to_bytes(8, sys.byteorder))[0]
+ pi_truth = array("d", pi_truth_double.to_bytes(8, sys.byteorder))[0]
-print(cos(math.pi))
+print("e:", e == e_truth or (e, e_truth, e - e_truth))
+print("pi:", pi == pi_truth or (pi, pi_truth, pi - pi_truth))
diff --git a/tests/float/math_constants_extra.py b/tests/float/math_constants_extra.py
index dea49aef5a..756cb45803 100644
--- a/tests/float/math_constants_extra.py
+++ b/tests/float/math_constants_extra.py
@@ -9,9 +9,12 @@ except (ImportError, AttributeError):
raise SystemExit
print(math.tau == 2.0 * math.pi)
+print(math.copysign(1.0, math.tau))
print(math.inf == float("inf"))
print(-math.inf == -float("inf"))
+print(math.copysign(1.0, math.inf))
print(isnan(math.nan))
print(isnan(-math.nan))
+print(math.copysign(1.0, math.nan))
diff --git a/tests/import/import_star.py b/tests/import/import_star.py
new file mode 100644
index 0000000000..0947f6a835
--- /dev/null
+++ b/tests/import/import_star.py
@@ -0,0 +1,59 @@
+# test `from package import *` conventions, including __all__ support
+#
+# This test requires MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES
+
+try:
+ next(iter([]), 42)
+except TypeError:
+ # 2-argument version of next() not supported
+ # we are probably not at MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES
+ print('SKIP')
+ raise SystemExit
+
+# 1. test default visibility
+from pkgstar_default import *
+
+print('visibleFun' in globals())
+print('VisibleClass' in globals())
+print('_hiddenFun' in globals())
+print('_HiddenClass' in globals())
+print(visibleFun())
+
+# 2. test explicit visibility as defined by __all__ (as an array)
+from pkgstar_all_array import *
+
+print('publicFun' in globals())
+print('PublicClass' in globals())
+print('unlistedFun' in globals())
+print('UnlistedClass' in globals())
+print('_privateFun' in globals())
+print('_PrivateClass' in globals())
+print(publicFun())
+# test dynamic import as used in asyncio
+print('dynamicFun' in globals())
+print(dynamicFun())
+
+# 3. test explicit visibility as defined by __all__ (as an tuple)
+from pkgstar_all_tuple import *
+
+print('publicFun2' in globals())
+print('PublicClass2' in globals())
+print('unlistedFun2' in globals())
+print('UnlistedClass2' in globals())
+print(publicFun2())
+
+# 4. test reporting of missing entries in __all__
+try:
+ from pkgstar_all_miss import *
+
+ print("missed detection of incorrect __all__ definition")
+except AttributeError as er:
+ print("AttributeError triggered for bad __all__ definition")
+
+# 5. test reporting of invalid __all__ definition
+try:
+ from pkgstar_all_inval import *
+
+ print("missed detection of incorrect __all__ definition")
+except TypeError as er:
+ print("TypeError triggered for bad __all__ definition")
diff --git a/tests/import/pkgstar_all_array/__init__.py b/tests/import/pkgstar_all_array/__init__.py
new file mode 100644
index 0000000000..4499a94d59
--- /dev/null
+++ b/tests/import/pkgstar_all_array/__init__.py
@@ -0,0 +1,49 @@
+__all__ = ['publicFun', 'PublicClass', 'dynamicFun']
+
+
+# Definitions below should always be imported by a star import
+def publicFun():
+ return 1
+
+
+class PublicClass:
+ def __init__(self):
+ self._val = 1
+
+
+# If __all__ support is enabled, definitions below
+# should not be imported by a star import
+def unlistedFun():
+ return 0
+
+
+class UnlistedClass:
+ def __init__(self):
+ self._val = 0
+
+
+# Definitions below should be not be imported by a star import
+# (they start with an underscore, and are not listed in __all__)
+def _privateFun():
+ return -1
+
+
+class _PrivateClass:
+ def __init__(self):
+ self._val = -1
+
+
+# Test lazy loaded function, as used by extmod/asyncio:
+# Works with a star import only if __all__ support is enabled
+_attrs = {
+ "dynamicFun": "funcs",
+}
+
+
+def __getattr__(attr):
+ mod = _attrs.get(attr, None)
+ if mod is None:
+ raise AttributeError(attr)
+ value = getattr(__import__(mod, globals(), locals(), True, 1), attr)
+ globals()[attr] = value
+ return value
diff --git a/tests/import/pkgstar_all_array/funcs.py b/tests/import/pkgstar_all_array/funcs.py
new file mode 100644
index 0000000000..7540d70f66
--- /dev/null
+++ b/tests/import/pkgstar_all_array/funcs.py
@@ -0,0 +1,2 @@
+def dynamicFun():
+ return 777
diff --git a/tests/import/pkgstar_all_inval/__init__.py b/tests/import/pkgstar_all_inval/__init__.py
new file mode 100644
index 0000000000..7022476c19
--- /dev/null
+++ b/tests/import/pkgstar_all_inval/__init__.py
@@ -0,0 +1 @@
+__all__ = 42
diff --git a/tests/import/pkgstar_all_miss/__init__.py b/tests/import/pkgstar_all_miss/__init__.py
new file mode 100644
index 0000000000..d960c7d0e2
--- /dev/null
+++ b/tests/import/pkgstar_all_miss/__init__.py
@@ -0,0 +1,8 @@
+__all__ = ('existingFun', 'missingFun')
+
+
+def existingFun():
+ return None
+
+
+# missingFun is not defined, should raise an error on import
diff --git a/tests/import/pkgstar_all_tuple/__init__.py b/tests/import/pkgstar_all_tuple/__init__.py
new file mode 100644
index 0000000000..a97715ed39
--- /dev/null
+++ b/tests/import/pkgstar_all_tuple/__init__.py
@@ -0,0 +1,22 @@
+__all__ = ('publicFun2', 'PublicClass2')
+
+
+# Definitions below should always be imported by a star import
+def publicFun2():
+ return 2
+
+
+class PublicClass2:
+ def __init__(self):
+ self._val = 2
+
+
+# If __all__ support is enabled, definitions below
+# should not be imported by a star import
+def unlistedFun2():
+ return 0
+
+
+class UnlistedClass2:
+ def __init__(self):
+ self._val = 0
diff --git a/tests/import/pkgstar_default/__init__.py b/tests/import/pkgstar_default/__init__.py
new file mode 100644
index 0000000000..4947e4ce7f
--- /dev/null
+++ b/tests/import/pkgstar_default/__init__.py
@@ -0,0 +1,20 @@
+# When __all__ is undefined, star import should only
+# show objects that do not start with an underscore
+
+
+def visibleFun():
+ return 42
+
+
+class VisibleClass:
+ def __init__(self):
+ self._val = 42
+
+
+def _hiddenFun():
+ return -1
+
+
+class _HiddenClass:
+ def __init__(self):
+ self._val = -1
diff --git a/tests/inlineasm/xtensa/asmargs.py b/tests/inlineasm/xtensa/asmargs.py
new file mode 100644
index 0000000000..2bfccfcc69
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmargs.py
@@ -0,0 +1,44 @@
+# test passing arguments
+
+
+@micropython.asm_xtensa
+def arg0():
+ movi(a2, 1)
+
+
+print(arg0())
+
+
+@micropython.asm_xtensa
+def arg1(a2):
+ addi(a2, a2, 1)
+
+
+print(arg1(1))
+
+
+@micropython.asm_xtensa
+def arg2(a2, a3):
+ add(a2, a2, a3)
+
+
+print(arg2(1, 2))
+
+
+@micropython.asm_xtensa
+def arg3(a2, a3, a4):
+ add(a2, a2, a3)
+ add(a2, a2, a4)
+
+
+print(arg3(1, 2, 3))
+
+
+@micropython.asm_xtensa
+def arg4(a2, a3, a4, a5):
+ add(a2, a2, a3)
+ add(a2, a2, a4)
+ add(a2, a2, a5)
+
+
+print(arg4(1, 2, 3, 4))
diff --git a/tests/inlineasm/xtensa/asmargs.py.exp b/tests/inlineasm/xtensa/asmargs.py.exp
new file mode 100644
index 0000000000..e33a6964f4
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmargs.py.exp
@@ -0,0 +1,5 @@
+1
+2
+3
+6
+10
diff --git a/tests/inlineasm/xtensa/asmarith.py b/tests/inlineasm/xtensa/asmarith.py
new file mode 100644
index 0000000000..1c0934eb7a
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmarith.py
@@ -0,0 +1,119 @@
+@micropython.asm_xtensa
+def f1(a2):
+ abs_(a2, a2)
+
+
+for value in (10, -10, 0):
+ print(f1(value))
+
+
+ADDMI_TEMPLATE = """
+@micropython.asm_xtensa
+def f1(a2) -> int:
+ addmi(a2, a2, {})
+print(f1(0))
+"""
+
+for value in (-32768, -32767, 32512, 32513, 0):
+ try:
+ exec(ADDMI_TEMPLATE.format(value))
+ except SyntaxError as error:
+ print(error)
+
+
+@micropython.asm_xtensa
+def a2(a2, a3) -> int:
+ addx2(a2, a2, a3)
+
+
+@micropython.asm_xtensa
+def a4(a2, a3) -> int:
+ addx4(a2, a2, a3)
+
+
+@micropython.asm_xtensa
+def a8(a2, a3) -> int:
+ addx8(a2, a2, a3)
+
+
+@micropython.asm_xtensa
+def s2(a2, a3) -> int:
+ subx2(a2, a2, a3)
+
+
+@micropython.asm_xtensa
+def s4(a2, a3) -> int:
+ subx4(a2, a2, a3)
+
+
+@micropython.asm_xtensa
+def s8(a2, a3) -> int:
+ subx8(a2, a2, a3)
+
+
+for first, second in ((100, 100), (-100, 100), (-100, -100), (100, -100)):
+ print("a2", a2(first, second))
+ print("a4", a4(first, second))
+ print("a8", a8(first, second))
+ print("s2", s2(first, second))
+ print("s4", s4(first, second))
+ print("s8", s8(first, second))
+
+
+@micropython.asm_xtensa
+def f5(a2) -> int:
+ neg(a2, a2)
+
+
+for value in (0, -100, 100):
+ print(f5(value))
+
+
+@micropython.asm_xtensa
+def f6():
+ movi(a2, 0x100)
+ movi(a3, 1)
+ add(a2, a2, a3)
+ addi(a2, a2, 1)
+ addi(a2, a2, -2)
+ sub(a2, a2, a3)
+
+
+print(hex(f6()))
+
+
+@micropython.asm_xtensa
+def f7():
+ movi(a2, 0x10FF)
+ movi(a3, 1)
+ and_(a4, a2, a3)
+ or_(a4, a4, a3)
+ movi(a3, 0x200)
+ xor(a2, a4, a3)
+
+
+print(hex(f7()))
+
+
+@micropython.asm_xtensa
+def f8(a2, a3):
+ add_n(a2, a2, a3)
+
+
+print(f8(100, 200))
+
+
+@micropython.asm_xtensa
+def f9(a2):
+ addi_n(a2, a2, 1)
+
+
+print(f9(100))
+
+
+@micropython.asm_xtensa
+def f10(a2, a3) -> uint:
+ mull(a2, a2, a3)
+
+
+print(hex(f10(0xC0000000, 2)))
diff --git a/tests/inlineasm/xtensa/asmarith.py.exp b/tests/inlineasm/xtensa/asmarith.py.exp
new file mode 100644
index 0000000000..7aba46a27d
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmarith.py.exp
@@ -0,0 +1,40 @@
+10
+10
+0
+-32768
+-32767 is not a multiple of 256
+32512
+'addmi' integer 32513 isn't within range -32768..32512
+0
+a2 300
+a4 500
+a8 900
+s2 100
+s4 300
+s8 700
+a2 -100
+a4 -300
+a8 -700
+s2 -300
+s4 -500
+s8 -900
+a2 -300
+a4 -500
+a8 -900
+s2 -100
+s4 -300
+s8 -700
+a2 100
+a4 300
+a8 700
+s2 300
+s4 500
+s8 900
+0
+100
+-100
+0xff
+0x201
+300
+101
+0x80000000
diff --git a/tests/inlineasm/xtensa/asmbranch.py b/tests/inlineasm/xtensa/asmbranch.py
new file mode 100644
index 0000000000..22bcd5a7c7
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmbranch.py
@@ -0,0 +1,299 @@
+# test branch instructions
+
+
+@micropython.asm_xtensa
+def tball(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ ball(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tball(0xFFFFFFFF, 0xFFFFFFFF))
+print(tball(0xFFFEFFFF, 0xFFFFFFFF))
+print(tball(0x00000000, 0xFFFFFFFF))
+print(tball(0xFFFFFFFF, 0x01010101))
+
+
+@micropython.asm_xtensa
+def tbany(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bany(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbany(0xFFFFFFFF, 0xFFFFFFFF))
+print(tbany(0xFFFEFFFF, 0xFFFFFFFF))
+print(tbany(0x00000000, 0xFFFFFFFF))
+print(tbany(0xFFFFFFFF, 0x01010101))
+
+
+@micropython.asm_xtensa
+def tbbc(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bbc(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbbc(0xFFFFFFFF, 4))
+print(tbbc(0xFFFEFFFF, 16))
+print(tbbc(0x00000000, 1))
+
+
+BBCI_TEMPLATE = """
+@micropython.asm_xtensa
+def tbbci(a2) -> int:
+ mov(a3, a2)
+ movi(a2, 0)
+ bbci(a3, {}, end)
+ movi(a2, -1)
+ label(end)
+
+print(tbbci({}))
+"""
+
+
+for value, bit in ((0xFFFFFFFF, 4), (0xFFFEFFFF, 16), (0x00000000, 1)):
+ try:
+ exec(BBCI_TEMPLATE.format(bit, value))
+ except SyntaxError as error:
+ print(error)
+
+
+@micropython.asm_xtensa
+def tbbs(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bbs(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbbs(0x00000000, 4))
+print(tbbs(0x00010000, 16))
+print(tbbs(0xFFFFFFFF, 1))
+
+
+BBSI_TEMPLATE = """
+@micropython.asm_xtensa
+def tbbsi(a2) -> int:
+ mov(a3, a2)
+ movi(a2, 0)
+ bbsi(a3, {}, end)
+ movi(a2, -1)
+ label(end)
+
+print(tbbsi({}))
+"""
+
+
+for value, bit in ((0x00000000, 4), (0x00010000, 16), (0xFFFFFFFF, 1)):
+ try:
+ exec(BBSI_TEMPLATE.format(bit, value))
+ except SyntaxError as error:
+ print(error)
+
+
+@micropython.asm_xtensa
+def tbeq(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ beq(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbeq(0x00000000, 0x00000000))
+print(tbeq(0x00010000, 0x00000000))
+print(tbeq(0xFFFFFFFF, 0xFFFFFFFF))
+
+
+@micropython.asm_xtensa
+def tbeqz(a2) -> int:
+ mov(a3, a2)
+ movi(a2, 0)
+ beqz(a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbeqz(0))
+print(tbeqz(0x12345678))
+print(tbeqz(-1))
+
+
+@micropython.asm_xtensa
+def tbge(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bge(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbge(0x00000000, 0x00000000))
+print(tbge(0x00010000, 0x00000000))
+print(tbge(0xF0000000, 0xFFFFFFFF))
+
+
+@micropython.asm_xtensa
+def tbgeu(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bgeu(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbgeu(0x00000000, 0x00000000))
+print(tbgeu(0x00010000, 0x00000000))
+print(tbgeu(0xF0000000, 0xFFFFFFFF))
+print(tbgeu(0xFFFFFFFF, 0xF0000000))
+
+
+@micropython.asm_xtensa
+def tbgez(a2) -> int:
+ mov(a3, a2)
+ movi(a2, 0)
+ bgez(a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbgez(0))
+print(tbgez(0x12345678))
+print(tbgez(-1))
+
+
+@micropython.asm_xtensa
+def tblt(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ blt(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tblt(0x00000000, 0x00000000))
+print(tblt(0x00010000, 0x00000000))
+print(tblt(0xF0000000, 0xFFFFFFFF))
+
+
+@micropython.asm_xtensa
+def tbltu(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bltu(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbltu(0x00000000, 0x00000000))
+print(tbltu(0x00010000, 0x00000000))
+print(tbltu(0xF0000000, 0xFFFFFFFF))
+print(tbltu(0xFFFFFFFF, 0xF0000000))
+
+
+@micropython.asm_xtensa
+def tbltz(a2) -> int:
+ mov(a3, a2)
+ movi(a2, 0)
+ bltz(a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbltz(0))
+print(tbltz(0x12345678))
+print(tbltz(-1))
+
+
+@micropython.asm_xtensa
+def tbnall(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bnall(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbnall(0xFFFFFFFF, 0xFFFFFFFF))
+print(tbnall(0xFFFEFFFF, 0xFFFFFFFF))
+print(tbnall(0x00000000, 0xFFFFFFFF))
+print(tbnall(0xFFFFFFFF, 0x01010101))
+
+
+@micropython.asm_xtensa
+def tbne(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bne(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbne(0x00000000, 0x00000000))
+print(tbne(0x00010000, 0x00000000))
+print(tbne(0xFFFFFFFF, 0xFFFFFFFF))
+
+
+@micropython.asm_xtensa
+def tbnez(a2) -> int:
+ mov(a3, a2)
+ movi(a2, 0)
+ bnez(a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbnez(0))
+print(tbnez(0x12345678))
+print(tbnez(-1))
+
+
+@micropython.asm_xtensa
+def tbnone(a2, a3) -> int:
+ mov(a4, a2)
+ movi(a2, 0)
+ bnone(a4, a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbnone(0xFFFFFFFF, 0xFFFFFFFF))
+print(tbnone(0xFFFEFFFF, 0xFFFFFFFF))
+print(tbnone(0x00000000, 0xFFFFFFFF))
+print(tbnone(0x10101010, 0x01010101))
+
+
+@micropython.asm_xtensa
+def tbeqz_n(a2) -> int:
+ mov(a3, a2)
+ movi(a2, 0)
+ beqz_n(a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbeqz_n(0))
+print(tbeqz_n(0x12345678))
+print(tbeqz_n(-1))
+
+
+@micropython.asm_xtensa
+def tbnez_n(a2) -> int:
+ mov(a3, a2)
+ movi(a2, 0)
+ bnez(a3, end)
+ movi(a2, -1)
+ label(end)
+
+
+print(tbnez_n(0))
+print(tbnez_n(0x12345678))
+print(tbnez_n(-1))
diff --git a/tests/inlineasm/xtensa/asmbranch.py.exp b/tests/inlineasm/xtensa/asmbranch.py.exp
new file mode 100644
index 0000000000..319a4b435e
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmbranch.py.exp
@@ -0,0 +1,66 @@
+0
+-1
+-1
+0
+0
+0
+-1
+0
+-1
+0
+0
+-1
+0
+0
+-1
+0
+0
+-1
+0
+0
+0
+-1
+0
+0
+-1
+-1
+0
+0
+-1
+0
+0
+-1
+0
+0
+0
+-1
+-1
+-1
+0
+-1
+-1
+0
+-1
+-1
+-1
+0
+-1
+0
+0
+-1
+-1
+0
+-1
+-1
+0
+0
+-1
+-1
+0
+0
+0
+-1
+-1
+-1
+0
+0
diff --git a/tests/inlineasm/xtensa/asmjump.py b/tests/inlineasm/xtensa/asmjump.py
new file mode 100644
index 0000000000..f41c948231
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmjump.py
@@ -0,0 +1,26 @@
+@micropython.asm_xtensa
+def jump() -> int:
+ movi(a2, 0)
+ j(NEXT)
+ addi(a2, a2, 1)
+ j(DONE)
+ label(NEXT)
+ addi(a2, a2, 2)
+ label(DONE)
+
+
+print(jump())
+
+
+@micropython.asm_xtensa
+def jumpx() -> int:
+ call0(ENTRY)
+ label(ENTRY)
+ movi(a2, 0)
+ addi(a3, a0, 12)
+ jx(a3)
+ movi(a2, 1)
+ movi(a2, 2)
+
+
+print(jumpx())
diff --git a/tests/inlineasm/xtensa/asmjump.py.exp b/tests/inlineasm/xtensa/asmjump.py.exp
new file mode 100644
index 0000000000..51993f072d
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmjump.py.exp
@@ -0,0 +1,2 @@
+2
+2
diff --git a/tests/inlineasm/xtensa/asmloadstore.py b/tests/inlineasm/xtensa/asmloadstore.py
new file mode 100644
index 0000000000..b185e30520
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmloadstore.py
@@ -0,0 +1,98 @@
+import array
+
+# On the 8266 the generated code gets put into the IRAM segment, which is only
+# word-addressable. Therefore, to test byte and halfword load/store opcodes
+# some memory must be reserved in the DRAM segment.
+
+BYTE_DATA = array.array("B", (0x11, 0x22, 0x33, 0x44))
+WORD_DATA = array.array("h", (100, 200, -100, -200))
+DWORD_DATA = array.array("i", (100_000, -200_000, 300_000, -400_000))
+
+
+@micropython.asm_xtensa
+def tl32r() -> int:
+ nop()
+ j(CODE)
+ align(4)
+ label(DATA)
+ data(1, 1, 2, 3, 4, 5, 6, 7)
+ align(4)
+ label(CODE)
+ nop_n()
+ nop_n()
+ l32r(a2, DATA)
+
+
+print(hex(tl32r()))
+
+
+@micropython.asm_xtensa
+def tl32i() -> uint:
+ call0(ENTRY)
+ label(ENTRY)
+ l32i(a2, a0, 0)
+
+
+print(hex(tl32i()))
+
+
+@micropython.asm_xtensa
+def tl8ui(a2) -> uint:
+ mov(a3, a2)
+ l8ui(a2, a3, 1)
+
+
+print(hex(tl8ui(BYTE_DATA)))
+
+
+@micropython.asm_xtensa
+def tl16ui(a2) -> uint:
+ mov(a3, a2)
+ l16ui(a2, a3, 2)
+
+
+print(tl16ui(WORD_DATA))
+
+
+@micropython.asm_xtensa
+def tl16si(a2) -> int:
+ mov(a3, a2)
+ l16si(a2, a3, 6)
+
+
+print(tl16si(WORD_DATA))
+
+
+@micropython.asm_xtensa
+def ts8i(a2, a3):
+ s8i(a3, a2, 1)
+
+
+ts8i(BYTE_DATA, 0xFF)
+print(BYTE_DATA)
+
+
+@micropython.asm_xtensa
+def ts16i(a2, a3):
+ s16i(a3, a2, 2)
+
+
+ts16i(WORD_DATA, -123)
+print(WORD_DATA)
+
+
+@micropython.asm_xtensa
+def ts32i(a2, a3) -> uint:
+ s32i(a3, a2, 4)
+
+
+ts32i(DWORD_DATA, -123456)
+print(DWORD_DATA)
+
+
+@micropython.asm_xtensa
+def tl32i_n(a2) -> uint:
+ l32i_n(a2, a2, 8)
+
+
+print(tl32i_n(DWORD_DATA))
diff --git a/tests/inlineasm/xtensa/asmloadstore.py.exp b/tests/inlineasm/xtensa/asmloadstore.py.exp
new file mode 100644
index 0000000000..e6672df6f8
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmloadstore.py.exp
@@ -0,0 +1,9 @@
+0x4030201
+0xf8002022
+0x22
+200
+-200
+array('B', [17, 255, 51, 68])
+array('h', [100, -123, -100, -200])
+array('i', [100000, -123456, 300000, -400000])
+300000
diff --git a/tests/inlineasm/xtensa/asmmisc.py b/tests/inlineasm/xtensa/asmmisc.py
new file mode 100644
index 0000000000..271ab83662
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmmisc.py
@@ -0,0 +1,25 @@
+@micropython.asm_xtensa
+def tnop(a2, a3, a4, a5):
+ nop()
+
+
+out2 = tnop(0x100, 0x200, 0x300, 0x400)
+print(out2 == 0x100)
+
+
+@micropython.asm_xtensa
+def tnop_n(a2, a3, a4, a5):
+ nop_n()
+
+
+out2 = tnop_n(0x100, 0x200, 0x300, 0x400)
+print(out2 == 0x100)
+
+
+@micropython.asm_xtensa
+def tmov_n(a2, a3):
+ mov_n(a4, a3)
+ add(a2, a4, a3)
+
+
+print(tmov_n(0, 1))
diff --git a/tests/inlineasm/xtensa/asmmisc.py.exp b/tests/inlineasm/xtensa/asmmisc.py.exp
new file mode 100644
index 0000000000..eefaa35daf
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmmisc.py.exp
@@ -0,0 +1,3 @@
+True
+True
+2
diff --git a/tests/inlineasm/xtensa/asmshift.py b/tests/inlineasm/xtensa/asmshift.py
new file mode 100644
index 0000000000..271ca1ccd4
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmshift.py
@@ -0,0 +1,137 @@
+@micropython.asm_xtensa
+def lsl1(a2):
+ slli(a2, a2, 1)
+
+
+print(hex(lsl1(0x123)))
+
+
+@micropython.asm_xtensa
+def lsl23(a2):
+ slli(a2, a2, 23)
+
+
+print(hex(lsl23(1)))
+
+
+@micropython.asm_xtensa
+def lsr1(a2):
+ srli(a2, a2, 1)
+
+
+print(hex(lsr1(0x123)))
+
+
+@micropython.asm_xtensa
+def lsr15(a2):
+ srli(a2, a2, 15)
+
+
+print(hex(lsr15(0x80000000)))
+
+
+@micropython.asm_xtensa
+def asr1(a2):
+ srai(a2, a2, 1)
+
+
+print(hex(asr1(0x123)))
+
+
+@micropython.asm_xtensa
+def asr31(a2):
+ srai(a2, a2, 31)
+
+
+print(hex(asr31(0x80000000)))
+
+
+@micropython.asm_xtensa
+def lsl1r(a2):
+ movi(a3, 1)
+ ssl(a3)
+ sll(a2, a2)
+
+
+print(hex(lsl1r(0x123)))
+
+
+@micropython.asm_xtensa
+def lsr1r(a2):
+ movi(a3, 1)
+ ssr(a3)
+ srl(a2, a2)
+
+
+print(hex(lsr1r(0x123)))
+
+
+@micropython.asm_xtensa
+def asr1r(a2):
+ movi(a3, 1)
+ ssr(a3)
+ sra(a2, a2)
+
+
+print(hex(asr1r(0x123)))
+
+
+@micropython.asm_xtensa
+def sll9(a2):
+ ssai(9)
+ sll(a2, a2)
+
+
+print(hex(sll9(1)))
+
+
+@micropython.asm_xtensa
+def srlr(a2, a3):
+ ssa8l(a3)
+ srl(a2, a2)
+
+
+print(hex(srlr(0x12340000, 2)))
+
+
+@micropython.asm_xtensa
+def sllr(a2, a3):
+ ssa8b(a3)
+ sll(a2, a2)
+
+
+print(hex(sllr(0x1234, 2)))
+
+
+@micropython.asm_xtensa
+def srcr(a2, a3, a4):
+ ssr(a4)
+ src(a2, a2, a3)
+
+
+print(hex(srcr(0x00000001, 0x80000000, 2)))
+
+
+@micropython.asm_xtensa
+def srai24(a2):
+ srai(a2, a2, 24)
+
+
+print(hex(srai24(0x12345678)))
+
+
+@micropython.asm_xtensa
+def nsar(a2, a3):
+ nsa(a2, a3)
+
+
+print(nsar(0x12345678, 0))
+print(nsar(0x12345678, -1))
+
+
+@micropython.asm_xtensa
+def nsaur(a2, a3):
+ nsau(a2, a3)
+
+
+print(nsaur(0x12345678, 0))
diff --git a/tests/inlineasm/xtensa/asmshift.py.exp b/tests/inlineasm/xtensa/asmshift.py.exp
new file mode 100644
index 0000000000..3e2bb3b4ae
--- /dev/null
+++ b/tests/inlineasm/xtensa/asmshift.py.exp
@@ -0,0 +1,17 @@
+0x246
+0x800000
+0x91
+0x10000
+0x91
+-0x1
+0x246
+0x91
+0x91
+0x800000
+0x1234
+0x12340000
+0x60000000
+0x12
+31
+31
+32
diff --git a/tests/micropython/viper_ptr16_load_boundary.py b/tests/micropython/viper_ptr16_load_boundary.py
new file mode 100644
index 0000000000..ccaaa0909a
--- /dev/null
+++ b/tests/micropython/viper_ptr16_load_boundary.py
@@ -0,0 +1,25 @@
+# Test boundary conditions for various architectures
+
+GET_TEMPLATE = """
+@micropython.viper
+def get{off}(src: ptr16) -> int:
+ return src[{off}]
+print(b[{off} * 2:({off} + 1) * 2])
+"""
+
+
+@micropython.viper
+def get_index(src: ptr16, i: int) -> int:
+ return src[i]
+
+
+b = bytearray(5000)
+b[28:38] = b"0123456789"
+b[252:262] = b"ABCDEFGHIJ"
+b[4092:4102] = b"KLMNOPQRST"
+
+for pre, idx, post in (15, 16, 17), (127, 128, 129), (2047, 2048, 2049):
+ print(get_index(b, pre), get_index(b, idx), get_index(b, post))
+ exec(GET_TEMPLATE.format(off=pre))
+ exec(GET_TEMPLATE.format(off=idx))
+ exec(GET_TEMPLATE.format(off=post))
diff --git a/tests/micropython/viper_ptr16_load_boundary.py.exp b/tests/micropython/viper_ptr16_load_boundary.py.exp
new file mode 100644
index 0000000000..4b8c184c13
--- /dev/null
+++ b/tests/micropython/viper_ptr16_load_boundary.py.exp
@@ -0,0 +1,12 @@
+13106 13620 14134
+bytearray(b'23')
+bytearray(b'45')
+bytearray(b'67')
+17475 17989 18503
+bytearray(b'CD')
+bytearray(b'EF')
+bytearray(b'GH')
+20045 20559 21073
+bytearray(b'MN')
+bytearray(b'OP')
+bytearray(b'QR')
diff --git a/tests/micropython/viper_ptr16_store_boundary.py b/tests/micropython/viper_ptr16_store_boundary.py
new file mode 100644
index 0000000000..e0a4f84557
--- /dev/null
+++ b/tests/micropython/viper_ptr16_store_boundary.py
@@ -0,0 +1,41 @@
+# Test boundary conditions for various architectures
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr16):
+ dest[{off}] = {val}
+set{off}(b)
+print(b[{off} * 2:({off} + 1) * 2])
+"""
+
+TEST_DATA = (
+ (15, (0x4241, 0x4443, 0x4645)),
+ (127, (0x4847, 0x4A49, 0x4C4B)),
+ (2047, (0x4E4D, 0x504F, 0x5251)),
+)
+
+
+@micropython.viper
+def set_index(dest: ptr16, i: int, val: int):
+ dest[i] = val
+
+
+@micropython.viper
+def set_index(dest: ptr16, i: int, val: int):
+ dest[i] = val
+
+
+b = bytearray(5000)
+for start, vals in TEST_DATA:
+ for i, v in enumerate(vals):
+ set_index(b, start + i, v)
+ print(b[(start + i) * 2 : (start + i + 1) * 2])
+
+
+for i in range(len(b)):
+ b[i] = 0
+
+
+for start, vals in TEST_DATA:
+ for i, v in enumerate(vals):
+ exec(SET_TEMPLATE.format(off=start + i, val=v + 0x0101))
diff --git a/tests/micropython/viper_ptr16_store_boundary.py.exp b/tests/micropython/viper_ptr16_store_boundary.py.exp
new file mode 100644
index 0000000000..b56fe6695f
--- /dev/null
+++ b/tests/micropython/viper_ptr16_store_boundary.py.exp
@@ -0,0 +1,18 @@
+bytearray(b'AB')
+bytearray(b'CD')
+bytearray(b'EF')
+bytearray(b'GH')
+bytearray(b'IJ')
+bytearray(b'KL')
+bytearray(b'MN')
+bytearray(b'OP')
+bytearray(b'QR')
+bytearray(b'BC')
+bytearray(b'DE')
+bytearray(b'FG')
+bytearray(b'HI')
+bytearray(b'JK')
+bytearray(b'LM')
+bytearray(b'NO')
+bytearray(b'PQ')
+bytearray(b'RS')
diff --git a/tests/micropython/viper_ptr32_load_boundary.py b/tests/micropython/viper_ptr32_load_boundary.py
new file mode 100644
index 0000000000..6954bd46b2
--- /dev/null
+++ b/tests/micropython/viper_ptr32_load_boundary.py
@@ -0,0 +1,25 @@
+# Test boundary conditions for various architectures
+
+GET_TEMPLATE = """
+@micropython.viper
+def get{off}(src: ptr32) -> int:
+ return src[{off}]
+print(b[{off} * 4:({off} + 1) * 4])
+"""
+
+
+@micropython.viper
+def get_index(src: ptr32, i: int) -> int:
+ return src[i]
+
+
+b = bytearray(5000)
+b[24:43] = b"0123456789ABCDEFGHIJ"
+b[248:268] = b"KLMNOPQRSTUVWXYZabcd"
+b[4088:4108] = b"efghijklmnopqrstuvwx"
+
+for pre, idx, post in (7, 8, 9), (63, 64, 65), (1023, 1024, 1025):
+ print(get_index(b, pre), get_index(b, idx), get_index(b, post))
+ exec(GET_TEMPLATE.format(off=pre))
+ exec(GET_TEMPLATE.format(off=idx))
+ exec(GET_TEMPLATE.format(off=post))
diff --git a/tests/micropython/viper_ptr32_load_boundary.py.exp b/tests/micropython/viper_ptr32_load_boundary.py.exp
new file mode 100644
index 0000000000..a58e703f91
--- /dev/null
+++ b/tests/micropython/viper_ptr32_load_boundary.py.exp
@@ -0,0 +1,12 @@
+926299444 1111570744 1178944579
+bytearray(b'4567')
+bytearray(b'89AB')
+bytearray(b'CDEF')
+1381060687 1448432723 1515804759
+bytearray(b'OPQR')
+bytearray(b'STUV')
+bytearray(b'WXYZ')
+1818978921 1886350957 1953722993
+bytearray(b'ijkl')
+bytearray(b'mnop')
+bytearray(b'qrst')
diff --git a/tests/micropython/viper_ptr32_store_boundary.py b/tests/micropython/viper_ptr32_store_boundary.py
new file mode 100644
index 0000000000..243ff5cd9c
--- /dev/null
+++ b/tests/micropython/viper_ptr32_store_boundary.py
@@ -0,0 +1,35 @@
+# Test boundary conditions for various architectures
+
+TEST_DATA = (
+ (3, (0x04030201, 0x08070605, 0x0C0B0A09)),
+ (63, (0x100F0E0D, 0x14131211, 0x18171615)),
+ (1023, (0x1C1B1A19, 0x201F1E1D, 0x24232221)),
+)
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr32):
+ dest[{off}] = {val} & 0x3FFFFFFF
+set{off}(b)
+print(b[{off} * 4:({off} + 1) * 4])
+"""
+
+
+@micropython.viper
+def set_index(dest: ptr32, i: int, val: int):
+ dest[i] = val
+
+
+b = bytearray(5000)
+for start, vals in TEST_DATA:
+ for i, v in enumerate(vals):
+ set_index(b, start + i, v)
+ print(b[(start + i) * 4 : (start + i + 1) * 4])
+
+for i in range(len(b)):
+ b[i] = 0
+
+
+for start, vals in TEST_DATA:
+ for i, v in enumerate(vals):
+ exec(SET_TEMPLATE.format(off=start + i, val=v + 0x01010101))
diff --git a/tests/micropython/viper_ptr32_store_boundary.py.exp b/tests/micropython/viper_ptr32_store_boundary.py.exp
new file mode 100644
index 0000000000..89f09fbc7a
--- /dev/null
+++ b/tests/micropython/viper_ptr32_store_boundary.py.exp
@@ -0,0 +1,18 @@
+bytearray(b'\x01\x02\x03\x04')
+bytearray(b'\x05\x06\x07\x08')
+bytearray(b'\t\n\x0b\x0c')
+bytearray(b'\r\x0e\x0f\x10')
+bytearray(b'\x11\x12\x13\x14')
+bytearray(b'\x15\x16\x17\x18')
+bytearray(b'\x19\x1a\x1b\x1c')
+bytearray(b'\x1d\x1e\x1f ')
+bytearray(b'!"#$')
+bytearray(b'\x02\x03\x04\x05')
+bytearray(b'\x06\x07\x08\t')
+bytearray(b'\n\x0b\x0c\r')
+bytearray(b'\x0e\x0f\x10\x11')
+bytearray(b'\x12\x13\x14\x15')
+bytearray(b'\x16\x17\x18\x19')
+bytearray(b'\x1a\x1b\x1c\x1d')
+bytearray(b'\x1e\x1f !')
+bytearray(b'"#$%')
diff --git a/tests/micropython/viper_ptr8_load_boundary.py b/tests/micropython/viper_ptr8_load_boundary.py
new file mode 100644
index 0000000000..bcb17a1e1f
--- /dev/null
+++ b/tests/micropython/viper_ptr8_load_boundary.py
@@ -0,0 +1,25 @@
+# Test boundary conditions for various architectures
+
+GET_TEMPLATE = """
+@micropython.viper
+def get{off}(src: ptr8) -> int:
+ return src[{off}]
+print(get{off}(b))
+"""
+
+
+@micropython.viper
+def get_index(src: ptr8, i: int) -> int:
+ return src[i]
+
+
+b = bytearray(5000)
+b[30:32] = b"123"
+b[254:256] = b"456"
+b[4094:4096] = b"789"
+
+for pre, idx, post in (30, 31, 32), (254, 255, 256), (4094, 4095, 4096):
+ print(get_index(b, pre), get_index(b, idx), get_index(b, post))
+ exec(GET_TEMPLATE.format(off=pre))
+ exec(GET_TEMPLATE.format(off=idx))
+ exec(GET_TEMPLATE.format(off=post))
diff --git a/tests/micropython/viper_ptr8_load_boundary.py.exp b/tests/micropython/viper_ptr8_load_boundary.py.exp
new file mode 100644
index 0000000000..7cbd1ac78c
--- /dev/null
+++ b/tests/micropython/viper_ptr8_load_boundary.py.exp
@@ -0,0 +1,12 @@
+49 50 51
+49
+50
+51
+52 53 54
+52
+53
+54
+55 56 57
+55
+56
+57
diff --git a/tests/micropython/viper_ptr8_store_boundary.py b/tests/micropython/viper_ptr8_store_boundary.py
new file mode 100644
index 0000000000..ad51268454
--- /dev/null
+++ b/tests/micropython/viper_ptr8_store_boundary.py
@@ -0,0 +1,30 @@
+# Test boundary conditions for various architectures
+
+TEST_DATA = ((49, 30, 3), (52, 254, 3), (55, 4094, 3))
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr8):
+ dest[{off}] = {val}
+set{off}(b)
+print(b[{off}])
+"""
+
+
+@micropython.viper
+def set_index(dest: ptr8, i: int, val: int):
+ dest[i] = val
+
+
+b = bytearray(5000)
+for val, start, count in TEST_DATA:
+ for i in range(count):
+ set_index(b, start + i, val + i)
+ print(b[start : start + count])
+
+for i in range(len(b)):
+ b[i] = 0
+
+for val, start, count in TEST_DATA:
+ for i in range(count):
+ exec(SET_TEMPLATE.format(off=start + i, val=val + i + 16))
diff --git a/tests/micropython/viper_ptr8_store_boundary.py.exp b/tests/micropython/viper_ptr8_store_boundary.py.exp
new file mode 100644
index 0000000000..a35cb3ac9e
--- /dev/null
+++ b/tests/micropython/viper_ptr8_store_boundary.py.exp
@@ -0,0 +1,12 @@
+bytearray(b'123')
+bytearray(b'456')
+bytearray(b'789')
+65
+66
+67
+68
+69
+70
+71
+72
+73
diff --git a/tests/misc/print_exception.py b/tests/misc/print_exception.py
index 1d196d6ab1..92754733b5 100644
--- a/tests/misc/print_exception.py
+++ b/tests/misc/print_exception.py
@@ -1,3 +1,5 @@
+# Test sys.print_exception (MicroPython) / traceback.print_exception (CPython).
+
try:
import io
import sys
diff --git a/tests/multi_net/tcp_accept_recv.py b/tests/multi_net/tcp_accept_recv.py
index dee14e3b97..4108a6f8a3 100644
--- a/tests/multi_net/tcp_accept_recv.py
+++ b/tests/multi_net/tcp_accept_recv.py
@@ -1,30 +1,73 @@
-# Test recv on socket that just accepted a connection
+# Test recv on listening socket after accept(), with various listen() arguments
import socket
PORT = 8000
+# Test cases for listen() function
+LISTEN_ARGS = [None, 0, 1, 2] # None means no argument
+
# Server
def instance0():
multitest.globals(IP=multitest.get_network_ip())
- s = socket.socket()
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
- s.listen(1)
multitest.next()
- s.accept()
- try:
- print("recv", s.recv(10)) # should raise Errno 107 ENOTCONN
- except OSError as er:
- print(er.errno in (107, 128))
- s.close()
+
+ test_num = 0
+ for blocking_mode in [True, False]:
+ for listen_arg in LISTEN_ARGS:
+ test_num += 1
+ s = socket.socket()
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
+
+ # Call listen with or without argument based on test case
+ if listen_arg is None:
+ print(f"Test case {test_num}/8: listen() blocking={blocking_mode}")
+ s.listen()
+ else:
+ print(f"Test case {test_num}/8: listen({listen_arg}) blocking={blocking_mode}")
+ s.listen(listen_arg)
+
+ # Signal client that server is ready
+ multitest.broadcast(f"server_ready_{test_num}")
+
+ # Wait for client connection
+ c, _ = s.accept()
+
+ # Set blocking mode after accept
+ s.setblocking(blocking_mode)
+
+ try:
+ print("recv", s.recv(10)) # should raise Errno 107 ENOTCONN
+ except OSError as er:
+ # Verify the error code is either 107 (ENOTCONN) or 128 (ENOTCONN on Windows)
+ print(er.errno in (107, 128))
+
+ # Cleanup
+ c.close()
+ s.close()
+
+ # Signal client we're done with this test case
+ multitest.broadcast(f"server_done_{test_num}")
# Client
def instance1():
multitest.next()
- s = socket.socket()
- s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
- s.send(b"GET / HTTP/1.0\r\n\r\n")
- s.close()
+
+ test_num = 0
+ for blocking_mode in [True, False]:
+ for _ in LISTEN_ARGS:
+ test_num += 1
+ # Wait for server to be ready
+ multitest.wait(f"server_ready_{test_num}")
+
+ # Connect to server
+ s = socket.socket()
+ s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
+ s.send(b"GET / HTTP/1.0\r\n\r\n")
+ s.close()
+
+ # Wait for server to finish this test case
+ multitest.wait(f"server_done_{test_num}")
diff --git a/tests/multi_net/tcp_recv_peek.py b/tests/multi_net/tcp_recv_peek.py
new file mode 100644
index 0000000000..ff540dd3c3
--- /dev/null
+++ b/tests/multi_net/tcp_recv_peek.py
@@ -0,0 +1,46 @@
+# Test TCP recv with MSG_PEEK
+#
+# Note that bare metal LWIP only returns at most one packet's worth of TCP data
+# in any recv() call - including when peeking - so can't be too clever with
+# different recv() combinations
+import socket
+import random
+
+
+# Server
+def instance0():
+ PORT = random.randrange(10000, 50000)
+ multitest.globals(IP=multitest.get_network_ip(), PORT=PORT)
+ s = socket.socket()
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
+ s.listen()
+ multitest.next()
+ s2, _ = s.accept()
+ print(s2.recv(8, socket.MSG_PEEK))
+ print(s2.recv(8))
+ s2.send(b"1234567890")
+ multitest.broadcast("0-sent")
+ multitest.wait("1-sent")
+ print(s2.recv(5, socket.MSG_PEEK))
+ print(s2.recv(5, socket.MSG_PEEK))
+ multitest.broadcast("0-recved")
+ multitest.wait("1-recved") # sync here necessary as MP sends RST if closing TCP early
+ s2.close()
+ s.close()
+
+
+# Client
+def instance1():
+ multitest.next()
+ s = socket.socket()
+ s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
+ s.send(b"abcdefgh")
+ multitest.broadcast("1-sent")
+ multitest.wait("0-sent")
+ s.send(b"klmnopqr")
+ print(s.recv(5, socket.MSG_PEEK))
+ print(s.recv(10))
+ multitest.broadcast("1-recved")
+ multitest.wait("0-recved")
+ s.close()
diff --git a/tests/multi_net/udp_data_multi.py b/tests/multi_net/udp_data_multi.py
new file mode 100644
index 0000000000..5d7b13e518
--- /dev/null
+++ b/tests/multi_net/udp_data_multi.py
@@ -0,0 +1,69 @@
+# Test UDP reception when there are multiple incoming UDP packets that need to be
+# queued internally in the TCP/IP stack.
+
+import socket
+
+NUM_NEW_SOCKETS = 4
+NUM_PACKET_BURSTS = 6
+NUM_PACKET_GROUPS = 5
+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
+PORT = 8000
+
+
+# Server
+def instance0():
+ recv_count = {i: 0 for i in range(NUM_PACKET_GROUPS)}
+ multitest.globals(IP=multitest.get_network_ip())
+ multitest.next()
+ for i in range(NUM_NEW_SOCKETS):
+ print("test socket", i)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT + i)[0][-1])
+ s.settimeout(0.250)
+ multitest.broadcast("server ready")
+ for burst in range(NUM_PACKET_BURSTS):
+ # Wait for all packets to be sent, without receiving any yet.
+ multitest.wait("data sent burst={}".format(burst))
+ # Try to receive all packets (they should be waiting in the queue).
+ for group in range(NUM_PACKET_GROUPS):
+ try:
+ data, addr = s.recvfrom(1000)
+ except:
+ continue
+ recv_burst, recv_group = data.split(b":")
+ recv_burst = int(recv_burst)
+ recv_group = int(recv_group)
+ if recv_burst == burst:
+ recv_count[recv_group] += 1
+ # Inform the client that all data was received.
+ multitest.broadcast("data received burst={}".format(burst))
+ s.close()
+
+ # Check how many packets were received.
+ for group, count in recv_count.items():
+ if count >= PACKET_RECV_THRESH:
+ print("pass group={}".format(group))
+ else:
+ print("fail group={} received={}%".format(group, 100 * count // TOTAL_PACKET_BURSTS))
+
+
+# Client
+def instance1():
+ multitest.next()
+ for i in range(NUM_NEW_SOCKETS):
+ print("test socket", i)
+ ai = socket.getaddrinfo(IP, PORT + i)[0][-1]
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ multitest.wait("server ready")
+ for burst in range(NUM_PACKET_BURSTS):
+ # Send a bunch of packets all in a row.
+ for group in range(NUM_PACKET_GROUPS):
+ s.sendto(b"%d:%d" % (burst, group), ai)
+ # Inform the server that the data has been sent.
+ multitest.broadcast("data sent burst={}".format(burst))
+ # Wait for the server to finish receiving.
+ multitest.wait("data received burst={}".format(burst))
+ s.close()
diff --git a/tests/multi_net/udp_data_multi.py.exp b/tests/multi_net/udp_data_multi.py.exp
new file mode 100644
index 0000000000..bc67c6ab0c
--- /dev/null
+++ b/tests/multi_net/udp_data_multi.py.exp
@@ -0,0 +1,15 @@
+--- instance0 ---
+test socket 0
+test socket 1
+test socket 2
+test socket 3
+pass group=0
+pass group=1
+pass group=2
+pass group=3
+pass group=4
+--- instance1 ---
+test socket 0
+test socket 1
+test socket 2
+test socket 3
diff --git a/tests/multi_net/udp_recv_dontwait.py b/tests/multi_net/udp_recv_dontwait.py
new file mode 100644
index 0000000000..640f3f060e
--- /dev/null
+++ b/tests/multi_net/udp_recv_dontwait.py
@@ -0,0 +1,59 @@
+# Test UDP recv and recvfrom with MSG_DONTWAIT
+import random
+import socket
+
+try:
+ import errno, time
+except ImportError:
+ print("SKIP")
+ raise SystemExit
+
+
+# Server
+def instance0():
+ PORT = random.randrange(10000, 50000)
+ multitest.globals(IP=multitest.get_network_ip(), PORT=PORT)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
+ multitest.next()
+ begin = time.ticks_ms()
+
+ # do some recvs before instance1 starts, when we know no packet is waiting
+ try:
+ print(s.recvfrom(8, socket.MSG_DONTWAIT))
+ except OSError as e:
+ print(e.errno == errno.EAGAIN)
+ try:
+ print(s.recv(8, socket.MSG_DONTWAIT))
+ except OSError as e:
+ print(e.errno == errno.EAGAIN)
+
+ # the above steps should not have taken any substantial time
+ elapsed = time.ticks_diff(time.ticks_ms(), begin)
+ print(True if elapsed < 50 else elapsed)
+
+ # Now instance1 will send us a UDP packet
+ multitest.broadcast("0-ready")
+ multitest.wait("1-sent")
+
+ for _ in range(10): # retry if necessary, to allow for network delay
+ time.sleep_ms(100)
+ try:
+ print(s.recv(8, socket.MSG_DONTWAIT))
+ break
+ except OSError as er:
+ if er.errno != errno.EAGAIN:
+ raise er
+ s.close()
+
+
+# Client
+def instance1():
+ multitest.next()
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
+ multitest.wait("0-ready")
+ print(s.send(b"abcdefgh"))
+ multitest.broadcast("1-sent")
+ s.close()
diff --git a/tests/multi_net/udp_recv_dontwait.py.exp b/tests/multi_net/udp_recv_dontwait.py.exp
new file mode 100644
index 0000000000..f61fd4bbe2
--- /dev/null
+++ b/tests/multi_net/udp_recv_dontwait.py.exp
@@ -0,0 +1,7 @@
+--- instance0 ---
+True
+True
+True
+b'abcdefgh'
+--- instance1 ---
+8
diff --git a/tests/multi_net/udp_recv_peek.py b/tests/multi_net/udp_recv_peek.py
new file mode 100644
index 0000000000..47897ce553
--- /dev/null
+++ b/tests/multi_net/udp_recv_peek.py
@@ -0,0 +1,36 @@
+# Test UDP recv and recvfrom with MSG_PEEK
+import random
+import socket
+import time
+
+
+# Server
+def instance0():
+ PORT = random.randrange(10000, 50000)
+ multitest.globals(IP=multitest.get_network_ip(), PORT=PORT)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
+ multitest.next()
+ peek_bytes, peek_addr = s.recvfrom(8, socket.MSG_PEEK)
+ print(peek_bytes)
+ real_bytes, real_addr = s.recvfrom(8)
+ print(real_bytes)
+ print(peek_addr == real_addr) # source addr should be the same for each
+ res = s.sendto(b"1234567890", peek_addr)
+ print(res)
+ print(s.recv(5, socket.MSG_PEEK))
+ print(s.recv(5, socket.MSG_PEEK))
+ s.close()
+
+
+# Client
+def instance1():
+ multitest.next()
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
+ s.send(b"abcdefgh")
+ s.send(b"klmnopqr")
+ print(s.recv(5, socket.MSG_PEEK))
+ print(s.recv(10))
+ s.close()
diff --git a/tests/net_inet/mpycert.der b/tests/net_inet/mpycert.der
index 0b0eabc9bc..ac22dcf9e8 100644
--- a/tests/net_inet/mpycert.der
+++ b/tests/net_inet/mpycert.der
Binary files differ
diff --git a/tests/net_inet/ssl_cert.py b/tests/net_inet/ssl_cert.py
index 3597b7855d..83d83e3f8e 100644
--- a/tests/net_inet/ssl_cert.py
+++ b/tests/net_inet/ssl_cert.py
@@ -1,4 +1,3 @@
-import binascii
import socket
import ssl
@@ -6,48 +5,48 @@ import ssl
# This certificate was obtained from micropython.org using openssl:
# $ openssl s_client -showcerts -connect micropython.org:443 </dev/null 2>/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=R10
# i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
-# a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
+# a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption
# v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT
-# Copy PEM content to a file (certmpy.pem) and convert to DER e.g.
-# $ openssl x509 -in certmpy.pem -out certmpy.der -outform DER
-# Then convert to hex format, eg using binascii.hexlify(data).
+# Copy PEM content to a file (mpycert.pem) and convert to DER e.g.
+# $ openssl x509 -in mpycert.pem -out mpycert.der -outform DER
+# Then convert to hex format using: for i in range(0,len(data),40):print(data[i:i+40].hex())
-ca_cert_chain = binascii.unhexlify(
- b"30820506308202eea0030201020211008a7d3e13d62f30ef2386bd29076b34f8300d06092a864886"
- b"f70d01010b0500304f310b300906035504061302555331293027060355040a1320496e7465726e65"
- b"742053656375726974792052657365617263682047726f7570311530130603550403130c49535247"
- b"20526f6f74205831301e170d3234303331333030303030305a170d3237303331323233353935395a"
- b"3033310b300906035504061302555331163014060355040a130d4c6574277320456e637279707431"
- b"0c300a0603550403130352313130820122300d06092a864886f70d01010105000382010f00308201"
- b"0a0282010100ba87bc5c1b0039cbca0acdd46710f9013ca54ea561cb26ca52fb1501b7b928f5281e"
- b"ed27b324183967090c08ece03ab03b770ebdf3e53954410c4eae41d69974de51dbef7bff58bda8b7"
- b"13f6de31d5f272c9726a0b8374959c4600641499f3b1d922d9cda892aa1c267a3ffeef58057b0895"
- b"81db710f8efbe33109bb09be504d5f8f91763d5a9d9e83f2e9c466b3e106664348188065a037189a"
- b"9b843297b1b2bdc4f815009d2788fbe26317966c9b27674bc4db285e69c279f0495ce02450e1c4bc"
- b"a105ac7b406d00b4c2413fa758b82fc55c9ba5bb099ef1feebb08539fda80aef45c478eb652ac2cf"
- b"5f3cdee35c4d1bf70b272baa0b4277534f796a1d87d90203010001a381f83081f5300e0603551d0f"
- b"0101ff040403020186301d0603551d250416301406082b0601050507030206082b06010505070301"
- b"30120603551d130101ff040830060101ff020100301d0603551d0e04160414c5cf46a4eaf4c3c07a"
- b"6c95c42db05e922f26e3b9301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58"
- b"f6e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a2f"
- b"2f78312e692e6c656e63722e6f72672f30130603551d20040c300a3008060667810c010201302706"
- b"03551d1f0420301e301ca01aa0188616687474703a2f2f78312e632e6c656e63722e6f72672f300d"
- b"06092a864886f70d01010b050003820201004ee2895d0a031c9038d0f51ff9715cf8c38fb237887a"
- b"6fb0251fedbeb7d886068ee90984cd72bf81f3fccacf5348edbdf66942d4a5113e35c813b2921d05"
- b"5fea2ed4d8f849c3adf599969cef26d8e1b4240b48204dfcd354b4a9c621c8e1361bff77642917b9"
- b"f04bef5deacd79d0bf90bfbe23b290da4aa9483174a9440be1e2f62d8371a4757bd294c10519461c"
- b"b98ff3c47448252a0de5f5db43e2db939bb919b41f2fdf6a0e8f31d3630fbb29dcdd662c3fb01b67"
- b"51f8413ce44db9acb8a49c6663f5ab85231dcc53b6ab71aedcc50171da36ee0a182a32fd09317c8f"
- b"f673e79c9cb54a156a77825acfda8d45fe1f2a6405303e73c2c60cb9d63b634aab4603fe99c04640"
- b"276063df503a0747d8154a9fea471f995a08620cb66c33084dd738ed482d2e0568ae805def4cdcd8"
- b"20415f68f1bb5acde30eb00c31879b43de4943e1c8043fd13c1b87453069a8a9720e79121c31d83e"
- b"2357dda74fa0f01c81d1771f6fd6d2b9a8b3031681394b9f55aed26ae4b3bfeaa5d59f4ba3c9d63b"
- b"72f34af654ab0cfc38f76080df6e35ca75a154e42fbc6e17c91aa537b5a29abaecf4c075464f77a8"
- b"e8595691662d6ede2981d6a697055e6445be2cceea644244b0c34fadf0b4dc03ca999b098295820d"
- b"638a66f91972f8d5b98910e289980935f9a21cbe92732374e99d1fd73b4a9a845810c2f3a7e235ec"
- b"7e3b45ce3046526bc0c0"
+ca_cert_chain = bytes.fromhex(
+ "30820505308202eda00302010202104ba85293f79a2fa273064ba8048d75d0300d06092a864886f7"
+ "0d01010b0500304f310b300906035504061302555331293027060355040a1320496e7465726e6574"
+ "2053656375726974792052657365617263682047726f7570311530130603550403130c4953524720"
+ "526f6f74205831301e170d3234303331333030303030305a170d3237303331323233353935395a30"
+ "33310b300906035504061302555331163014060355040a130d4c6574277320456e6372797074310c"
+ "300a0603550403130352313030820122300d06092a864886f70d01010105000382010f003082010a"
+ "0282010100cf57e5e6c45412edb447fec92758764650288c1d3e88df059dd5b51829bdddb55abffa"
+ "f6cea3beaf00214b625a5a3c012fc55803f689ff8e1143ebc1b5e01407968f6f1fd7e7ba81390975"
+ "65b7c2af185b372628e7a3f4072b6d1affab58bc95ae40ffe9cb57c4b55b7f780d1861bc17e754c6"
+ "bb4991cd6e18d18085eea66536bc74eabc504ceafc21f338169394bab0d36b3806cd16127aca5275"
+ "c8ad76b2c29c5d98455c6f617bc62dee3c13528601d957e6381cdf8db51f92919ae74a1ccc45a872"
+ "55f0b0e6a307ecfda71b669e3f488b71847158c93afaef5ef25b442b3c74e78fb247c1076acd9ab7"
+ "0d96f712812651540aec61f6f7f5e2f28ac8950d8d0203010001a381f83081f5300e0603551d0f01"
+ "01ff040403020186301d0603551d250416301406082b0601050507030206082b0601050507030130"
+ "120603551d130101ff040830060101ff020100301d0603551d0e04160414bbbcc347a5e4bca9c6c3"
+ "a4720c108da235e1c8e8301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58f6"
+ "e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a2f2f"
+ "78312e692e6c656e63722e6f72672f30130603551d20040c300a3008060667810c01020130270603"
+ "551d1f0420301e301ca01aa0188616687474703a2f2f78312e632e6c656e63722e6f72672f300d06"
+ "092a864886f70d01010b0500038202010092b1e74137eb799d81e6cde225e13a20e9904495a3815c"
+ "cfc35dfdbda070d5b19628220bd2f228cf0ce7d4e6438c24221dc14292d109af9f4bf4c8704f2016"
+ "b15add01f61ff81f616b1427b0728d63aeeee2ce4bcf37ddbba3d4cde7ad50adbdbfe3ec3e623670"
+ "9931a7e88dddea62e212aef59cd43d2c0caad09c79beea3d5c446e9631635a7dd67e4f24a04b057f"
+ "5e6fd2d4ea5f334b13d657b6cade51b85da3098274fdc7789eb3b9ac16da4a2b96c3b68b628ff974"
+ "19a29e03dee96f9bb00fd2a05af6855cc204b7c8d54e32c4bf045dbc29f6f7818f0c5d3c53c94090"
+ "8bfbb60865b9a421d509e51384843782ce1028fc76c206257a46524dda5372a4273f6270acbe6948"
+ "00fb670fdb5ba1e8d703212dd7c9f69942398343df770a1208f125d6ba9419541888a5c58ee11a99"
+ "93796bec1cf93140b0cc3200df9f5ee7b492ab9082918d0de01e95ba593b2e4b5fc2b74635523906"
+ "c0bdaaac52c122a0449799f70ca021a7a16c714716170168c0caa62665047cb3aec9e79455c26f9b"
+ "3c1ca9f92ec5201af076e0beec18d64fd825fb7611e8bfe6210fe8e8ccb5b6a7d5b8f79f41cf6122"
+ "466a83b668972e7cea4e95db23eb2ec82b2884a460e949f4442e3bf9ca625701e25d9016f9c9fc7a"
+ "23488ea6d58172f128fa5dcefbed4e738f942ed241949899dba7af705ff5befb0220bf66276cb4ad"
+ "fa75120b2b3ece039e"
)
diff --git a/tests/net_inet/test_sslcontext_client.py b/tests/net_inet/test_sslcontext_client.py
index 30ec0ac7c8..119a42721f 100644
--- a/tests/net_inet/test_sslcontext_client.py
+++ b/tests/net_inet/test_sslcontext_client.py
@@ -5,14 +5,12 @@ import ssl
# This certificate was obtained from micropython.org using openssl:
# $ openssl s_client -showcerts -connect micropython.org:443 </dev/null 2>/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=R10
# i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
-# a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
+# a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption
# v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT
-# Copy PEM content to a file (certmpy.pem) and convert to DER e.g.
-# $ openssl x509 -in certmpy.pem -out certmpy.der -outform DER
-# Then convert to hex format, eg using binascii.hexlify(data).
-
+# Copy PEM content to a file (mpycert.pem) and convert to DER e.g.
+# $ openssl x509 -in mpycert.pem -out mpycert.der -outform DER
ca_cert_chain = "mpycert.der"
try:
diff --git a/tests/ports/rp2/rp2_lightsleep_thread.py b/tests/ports/rp2/rp2_lightsleep_thread.py
new file mode 100644
index 0000000000..494ead4223
--- /dev/null
+++ b/tests/ports/rp2/rp2_lightsleep_thread.py
@@ -0,0 +1,67 @@
+# Verify that a thread running on CPU1 can go to lightsleep
+# and wake up in the expected timeframe
+import _thread
+import time
+import unittest
+from machine import lightsleep
+
+N_SLEEPS = 5
+SLEEP_MS = 250
+
+IDEAL_RUNTIME = N_SLEEPS * SLEEP_MS
+MAX_RUNTIME = (N_SLEEPS + 1) * SLEEP_MS
+MAX_DELTA = 20
+
+
+class LightSleepInThread(unittest.TestCase):
+ def thread_entry(self, is_thread=True):
+ for _ in range(N_SLEEPS):
+ lightsleep(SLEEP_MS)
+ if is_thread:
+ self.thread_done = True
+
+ def elapsed_ms(self):
+ return time.ticks_diff(time.ticks_ms(), self.t0)
+
+ def setUp(self):
+ self.thread_done = False
+ self.t0 = time.ticks_ms()
+
+ def test_cpu0_busy(self):
+ _thread.start_new_thread(self.thread_entry, ())
+ # CPU0 is busy-waiting not asleep itself
+ while not self.thread_done:
+ self.assertLessEqual(self.elapsed_ms(), MAX_RUNTIME)
+ self.assertAlmostEqual(self.elapsed_ms(), IDEAL_RUNTIME, delta=MAX_DELTA)
+
+ def test_cpu0_sleeping(self):
+ _thread.start_new_thread(self.thread_entry, ())
+ time.sleep_ms(MAX_RUNTIME)
+ self.assertTrue(self.thread_done)
+ self.assertAlmostEqual(self.elapsed_ms(), MAX_RUNTIME, delta=MAX_DELTA)
+
+ def test_cpu0_also_lightsleep(self):
+ _thread.start_new_thread(self.thread_entry, ())
+ time.sleep_ms(50) # account for any delay in starting the thread
+ self.thread_entry(False) # does the same lightsleep loop, doesn't set the done flag
+ while not self.thread_done:
+ time.sleep_ms(10)
+ #
+ # Only one thread can actually be in lightsleep at a time to avoid
+ # races, but otherwise the behaviour when both threads call lightsleep()
+ # is unspecified.
+ #
+ # Currently, the other thread will return immediately if one is already
+ # in lightsleep. Therefore, runtime can be between IDEAL_RUNTIME and
+ # IDEAL_RUNTIME * 2 depending on how many times the calls to lightsleep() race
+ # each other.
+ #
+ # Note this test case is really only here to ensure that the rp2 hasn't
+ # hung or failed to sleep at all - not to verify any correct behaviour
+ # when there's a race to call lightsleep().
+ self.assertGreaterEqual(self.elapsed_ms(), IDEAL_RUNTIME - MAX_DELTA)
+ self.assertLessEqual(self.elapsed_ms(), IDEAL_RUNTIME * 2 + MAX_DELTA)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/ports/rp2/rp2_machine_idle.py b/tests/ports/rp2/rp2_machine_idle.py
index 3135110b82..f9c2828478 100644
--- a/tests/ports/rp2/rp2_machine_idle.py
+++ b/tests/ports/rp2/rp2_machine_idle.py
@@ -1,4 +1,3 @@
-import sys
import machine
import time
@@ -18,11 +17,6 @@ import time
# Verification uses the average idle time, as individual iterations will always
# have outliers due to interrupts, scheduler, etc.
-# RP2350 currently fails this test because machine.idle() resumes immediately.
-if "RP2350" in sys.implementation._machine:
- print("SKIP")
- raise SystemExit
-
ITERATIONS = 500
total = 0
diff --git a/tests/ports/rp2/rp2_machine_timer.py b/tests/ports/rp2/rp2_machine_timer.py
new file mode 100644
index 0000000000..ac4efcf7f3
--- /dev/null
+++ b/tests/ports/rp2/rp2_machine_timer.py
@@ -0,0 +1,20 @@
+from machine import Timer
+from time import sleep_ms
+
+# Test the rp2-specific adjustable tick_hz and hard/soft IRQ handlers
+# for both one-shot and periodic timers.
+
+modes = {Timer.ONE_SHOT: "one-shot", Timer.PERIODIC: "periodic"}
+kinds = {False: "soft", True: "hard"}
+
+for mode in modes:
+ for hard in kinds:
+ for period in 2, 4:
+ timer = Timer(
+ mode=mode,
+ period=period,
+ hard=hard,
+ callback=lambda t: print("callback", modes[mode], kinds[hard], period),
+ )
+ sleep_ms(9)
+ timer.deinit()
diff --git a/tests/ports/rp2/rp2_machine_timer.py.exp b/tests/ports/rp2/rp2_machine_timer.py.exp
new file mode 100644
index 0000000000..b3dd93dfab
--- /dev/null
+++ b/tests/ports/rp2/rp2_machine_timer.py.exp
@@ -0,0 +1,16 @@
+callback one-shot soft 2
+callback one-shot soft 4
+callback one-shot hard 2
+callback one-shot hard 4
+callback periodic soft 2
+callback periodic soft 2
+callback periodic soft 2
+callback periodic soft 2
+callback periodic soft 4
+callback periodic soft 4
+callback periodic hard 2
+callback periodic hard 2
+callback periodic hard 2
+callback periodic hard 2
+callback periodic hard 4
+callback periodic hard 4
diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp
index 5ff947e883..ac64edde69 100644
--- a/tests/ports/unix/extra_coverage.py.exp
+++ b/tests/ports/unix/extra_coverage.py.exp
@@ -14,6 +14,7 @@ false true
80000000
abc
%
+.a .
# GC
0
0
@@ -50,16 +51,16 @@ RuntimeError:
ame__
port
-builtins micropython _asyncio _thread
-array binascii btree cexample
-cmath collections cppexample cryptolib
-deflate errno example_package
-ffi framebuf gc hashlib
-heapq io json machine
-marshal math os platform
-random re select socket
-struct sys termios time
-tls uctypes vfs websocket
+builtins micropython array binascii
+btree cexample cmath collections
+cppexample cryptolib deflate errno
+example_package ffi framebuf
+gc hashlib heapq io
+json machine marshal math
+os platform random re
+select socket struct sys
+termios time tls uctypes
+vfs websocket
me
micropython machine marshal math
@@ -89,6 +90,10 @@ data
12345
6
-1
+0
+1
+0
+0.000000
# runtime utils
TypeError: unsupported type for __abs__: 'str'
TypeError: unsupported types for __divmod__: 'str', 'str'
@@ -122,6 +127,13 @@ unlocked
KeyboardInterrupt:
KeyboardInterrupt:
10
+loop
+scheduled function
+loop
+scheduled function
+loop
+scheduled function
+scheduled function
# ringbuf
99 0
98 1
diff --git a/tests/run-multitests.py b/tests/run-multitests.py
index 387eec7018..92bd64193d 100755
--- a/tests/run-multitests.py
+++ b/tests/run-multitests.py
@@ -15,6 +15,8 @@ import itertools
import subprocess
import tempfile
+run_tests_module = __import__("run-tests")
+
test_dir = os.path.abspath(os.path.dirname(__file__))
if os.path.abspath(sys.path[0]) == test_dir:
@@ -488,9 +490,7 @@ def print_diff(a, b):
def run_tests(test_files, instances_truth, instances_test):
- skipped_tests = []
- passed_tests = []
- failed_tests = []
+ test_results = []
for test_file, num_instances in test_files:
instances_str = "|".join(str(instances_test[i]) for i in range(num_instances))
@@ -526,13 +526,13 @@ def run_tests(test_files, instances_truth, instances_test):
# Print result of test
if skip:
print("skip")
- skipped_tests.append(test_file)
+ test_results.append((test_file, "skip", ""))
elif output_test == output_truth:
print("pass")
- passed_tests.append(test_file)
+ test_results.append((test_file, "pass", ""))
else:
print("FAIL")
- failed_tests.append(test_file)
+ test_results.append((test_file, "fail", ""))
if not cmd_args.show_output:
print("### TEST ###")
print(output_test, end="")
@@ -549,15 +549,7 @@ def run_tests(test_files, instances_truth, instances_test):
if cmd_args.show_output:
print()
- print("{} tests performed".format(len(skipped_tests) + len(passed_tests) + len(failed_tests)))
- print("{} tests passed".format(len(passed_tests)))
-
- if skipped_tests:
- print("{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests)))
- if failed_tests:
- print("{} tests failed: {}".format(len(failed_tests), " ".join(failed_tests)))
-
- return not failed_tests
+ return test_results
def main():
@@ -583,6 +575,12 @@ def main():
default=1,
help="repeat the test with this many permutations of the instance order",
)
+ cmd_parser.add_argument(
+ "-r",
+ "--result-dir",
+ default=run_tests_module.base_path("results"),
+ help="directory for test results",
+ )
cmd_parser.epilog = (
"Supported instance types:\r\n"
" -i pyb:<port> physical device (eg. pyboard) on provided repl port.\n"
@@ -623,13 +621,15 @@ def main():
for _ in range(max_instances - len(instances_test)):
instances_test.append(PyInstanceSubProcess([MICROPYTHON]))
+ os.makedirs(cmd_args.result_dir, exist_ok=True)
all_pass = True
try:
for i, instances_test_permutation in enumerate(itertools.permutations(instances_test)):
if i >= cmd_args.permutations:
break
- all_pass &= run_tests(test_files, instances_truth, instances_test_permutation)
+ test_results = run_tests(test_files, instances_truth, instances_test_permutation)
+ all_pass &= run_tests_module.create_test_report(cmd_args, test_results)
finally:
for i in instances_truth:
diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py
index b858989daa..f9d2074f6f 100755
--- a/tests/run-natmodtests.py
+++ b/tests/run-natmodtests.py
@@ -9,6 +9,8 @@ import subprocess
import sys
import argparse
+run_tests_module = __import__("run-tests")
+
sys.path.append("../tools")
import pyboard
@@ -73,6 +75,7 @@ class __FS:
return __File()
vfs.mount(__FS(), '/__remote')
sys.path.insert(0, '/__remote')
+{import_prelude}
sys.modules['{}'] = __import__('__injected')
"""
@@ -132,7 +135,15 @@ def detect_architecture(target):
return platform, arch, None
-def run_tests(target_truth, target, args, stats, resolved_arch):
+def run_tests(target_truth, target, args, resolved_arch):
+ global injected_import_hook_code
+
+ prelude = ""
+ if args.begin:
+ prelude = args.begin.read()
+ injected_import_hook_code = injected_import_hook_code.replace("{import_prelude}", prelude)
+
+ test_results = []
for test_file in args.files:
# Find supported test
test_file_basename = os.path.basename(test_file)
@@ -155,7 +166,8 @@ def run_tests(target_truth, target, args, stats, resolved_arch):
with open(NATMOD_EXAMPLE_DIR + test_mpy, "rb") as f:
test_script += b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n"
except OSError:
- print("---- {} - mpy file not compiled".format(test_file))
+ test_results.append((test_file, "skip", "mpy file not compiled"))
+ print("skip {} - mpy file not compiled".format(test_file))
continue
test_script += bytes(injected_import_hook_code.format(test_module), "ascii")
test_script += test_file_data
@@ -187,17 +199,18 @@ def run_tests(target_truth, target, args, stats, resolved_arch):
result = "pass"
# Accumulate statistics
- stats["total"] += 1
if result == "pass":
- stats["pass"] += 1
+ test_results.append((test_file, "pass", ""))
elif result == "SKIP":
- stats["skip"] += 1
+ test_results.append((test_file, "skip", ""))
else:
- stats["fail"] += 1
+ test_results.append((test_file, "fail", ""))
# Print result
print("{:4} {}{}".format(result, test_file, extra))
+ return test_results
+
def main():
cmd_parser = argparse.ArgumentParser(
@@ -212,6 +225,19 @@ def main():
cmd_parser.add_argument(
"-a", "--arch", choices=AVAILABLE_ARCHS, help="override native architecture of the target"
)
+ cmd_parser.add_argument(
+ "-b",
+ "--begin",
+ type=argparse.FileType("rt"),
+ default=None,
+ help="prologue python file to execute before module import",
+ )
+ cmd_parser.add_argument(
+ "-r",
+ "--result-dir",
+ default=run_tests_module.base_path("results"),
+ help="directory for test results",
+ )
cmd_parser.add_argument("files", nargs="*", help="input test files")
args = cmd_parser.parse_args()
@@ -236,20 +262,14 @@ def main():
print("platform={} ".format(target_platform), end="")
print("arch={}".format(target_arch))
- stats = {"total": 0, "pass": 0, "fail": 0, "skip": 0}
- run_tests(target_truth, target, args, stats, target_arch)
+ os.makedirs(args.result_dir, exist_ok=True)
+ test_results = run_tests(target_truth, target, args, target_arch)
+ res = run_tests_module.create_test_report(args, test_results)
target.close()
target_truth.close()
- print("{} tests performed".format(stats["total"]))
- print("{} tests passed".format(stats["pass"]))
- if stats["fail"]:
- print("{} tests failed".format(stats["fail"]))
- if stats["skip"]:
- print("{} tests skipped".format(stats["skip"]))
-
- if stats["fail"]:
+ if not res:
sys.exit(1)
diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py
index 81d873c459..cac2fee58f 100755
--- a/tests/run-perfbench.py
+++ b/tests/run-perfbench.py
@@ -10,10 +10,12 @@ import sys
import argparse
from glob import glob
+run_tests_module = __import__("run-tests")
+
sys.path.append("../tools")
import pyboard
-prepare_script_for_target = __import__("run-tests").prepare_script_for_target
+prepare_script_for_target = run_tests_module.prepare_script_for_target
# Paths for host executables
if os.name == "nt":
@@ -90,9 +92,9 @@ def run_benchmark_on_target(target, script):
def run_benchmarks(args, target, param_n, param_m, n_average, test_list):
+ test_results = []
skip_complex = run_feature_test(target, "complex") != "complex"
skip_native = run_feature_test(target, "native_check") != "native"
- target_had_error = False
for test_file in sorted(test_list):
print(test_file + ": ", end="")
@@ -105,6 +107,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list):
and test_file.find("viper_") != -1
)
if skip:
+ test_results.append((test_file, "skip", ""))
print("SKIP")
continue
@@ -125,6 +128,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list):
if isinstance(target, pyboard.Pyboard) or args.via_mpy:
crash, test_script_target = prepare_script_for_target(args, script_text=test_script)
if crash:
+ test_results.append((test_file, "fail", "preparation"))
print("CRASH:", test_script_target)
continue
else:
@@ -162,10 +166,13 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list):
error = "FAIL truth"
if error is not None:
- if not error.startswith("SKIP"):
- target_had_error = True
+ if error.startswith("SKIP"):
+ test_results.append((test_file, "skip", error))
+ else:
+ test_results.append((test_file, "fail", error))
print(error)
else:
+ test_results.append((test_file, "pass", ""))
t_avg, t_sd = compute_stats(times)
s_avg, s_sd = compute_stats(scores)
print(
@@ -179,7 +186,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list):
sys.stdout.flush()
- return target_had_error
+ return test_results
def parse_output(filename):
@@ -265,6 +272,12 @@ def main():
cmd_parser.add_argument("--via-mpy", action="store_true", help="compile code to .mpy first")
cmd_parser.add_argument("--mpy-cross-flags", default="", help="flags to pass to mpy-cross")
cmd_parser.add_argument(
+ "-r",
+ "--result-dir",
+ default=run_tests_module.base_path("results"),
+ help="directory for test results",
+ )
+ cmd_parser.add_argument(
"N", nargs=1, help="N parameter (approximate target CPU frequency in MHz)"
)
cmd_parser.add_argument("M", nargs=1, help="M parameter (approximate target heap in kbytes)")
@@ -307,13 +320,15 @@ def main():
print("N={} M={} n_average={}".format(N, M, n_average))
- target_had_error = run_benchmarks(args, target, N, M, n_average, tests)
+ os.makedirs(args.result_dir, exist_ok=True)
+ test_results = run_benchmarks(args, target, N, M, n_average, tests)
+ res = run_tests_module.create_test_report(args, test_results)
if isinstance(target, pyboard.Pyboard):
target.exit_raw_repl()
target.close()
- if target_had_error:
+ if not res:
sys.exit(1)
diff --git a/tests/run-tests.py b/tests/run-tests.py
index ac411a0be6..628fde9d30 100755
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -105,14 +105,11 @@ PC_PLATFORMS = ("darwin", "linux", "win32")
# These are tests that are difficult to detect that they should not be run on the given target.
platform_tests_to_skip = {
"esp8266": (
- "micropython/viper_args.py", # too large
- "micropython/viper_binop_arith.py", # too large
- "misc/rge_sm.py", # too large
+ "misc/rge_sm.py", # incorrect values due to object representation C
),
"minimal": (
"basics/class_inplace_op.py", # all special methods not supported
"basics/subclass_native_init.py", # native subclassing corner cases not support
- "misc/rge_sm.py", # too large
"micropython/opt_level.py", # don't assume line numbers are stored
),
"nrf": (
@@ -272,22 +269,17 @@ def detect_test_platform(pyb, args):
print()
-def prepare_script_for_target(args, *, script_filename=None, script_text=None, force_plain=False):
+def prepare_script_for_target(args, *, script_text=None, force_plain=False):
if force_plain or (not args.via_mpy and args.emit == "bytecode"):
- if script_filename is not None:
- with open(script_filename, "rb") as f:
- script_text = f.read()
+ # A plain test to run as-is, no processing needed.
+ pass
elif args.via_mpy:
tempname = tempfile.mktemp(dir="")
mpy_filename = tempname + ".mpy"
- if script_filename is None:
- script_filename = tempname + ".py"
- cleanup_script_filename = True
- with open(script_filename, "wb") as f:
- f.write(script_text)
- else:
- cleanup_script_filename = False
+ script_filename = tempname + ".py"
+ with open(script_filename, "wb") as f:
+ f.write(script_text)
try:
subprocess.check_output(
@@ -303,8 +295,7 @@ def prepare_script_for_target(args, *, script_filename=None, script_text=None, f
script_text = b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n"
rm_f(mpy_filename)
- if cleanup_script_filename:
- rm_f(script_filename)
+ rm_f(script_filename)
script_text += bytes(injected_import_hook_code, "ascii")
else:
@@ -315,9 +306,21 @@ def prepare_script_for_target(args, *, script_filename=None, script_text=None, f
def run_script_on_remote_target(pyb, args, test_file, is_special):
- had_crash, script = prepare_script_for_target(
- args, script_filename=test_file, force_plain=is_special
- )
+ with open(test_file, "rb") as f:
+ script = f.read()
+
+ # If the test is not a special test, prepend it with a print to indicate that it started.
+ # If the print does not execute this means that the test did not even start, eg it was
+ # too large for the target.
+ prepend_start_test = not is_special
+ if prepend_start_test:
+ if script.startswith(b"#"):
+ script = b"print('START TEST')" + script
+ else:
+ script = b"print('START TEST')\n" + script
+
+ had_crash, script = prepare_script_for_target(args, script_text=script, force_plain=is_special)
+
if had_crash:
return True, script
@@ -328,9 +331,19 @@ def run_script_on_remote_target(pyb, args, test_file, is_special):
except pyboard.PyboardError as e:
had_crash = True
if not is_special and e.args[0] == "exception":
- output_mupy = e.args[1] + e.args[2] + b"CRASH"
+ if prepend_start_test and e.args[1] == b"" and b"MemoryError" in e.args[2]:
+ output_mupy = b"SKIP-TOO-LARGE\n"
+ else:
+ output_mupy = e.args[1] + e.args[2] + b"CRASH"
else:
output_mupy = bytes(e.args[0], "ascii") + b"\nCRASH"
+
+ if prepend_start_test:
+ if output_mupy.startswith(b"START TEST\r\n"):
+ output_mupy = output_mupy.removeprefix(b"START TEST\r\n")
+ else:
+ had_crash = True
+
return had_crash, output_mupy
@@ -392,6 +405,10 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False):
return rv
def send_get(what):
+ # Detect {\x00} pattern and convert to ctrl-key codes.
+ ctrl_code = lambda m: bytes([int(m.group(1))])
+ what = re.sub(rb'{\\x(\d\d)}', ctrl_code, what)
+
os.write(master, what)
return get()
@@ -474,7 +491,7 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False):
output_mupy = output_mupy.replace(b"\r\n", b"\n")
# don't try to convert the output if we should skip this test
- if had_crash or output_mupy in (b"SKIP\n", b"CRASH"):
+ if had_crash or output_mupy in (b"SKIP\n", b"SKIP-TOO-LARGE\n", b"CRASH"):
return output_mupy
# skipped special tests will output "SKIP" surrounded by other interpreter debug output
@@ -603,11 +620,8 @@ class PyboardNodeRunner:
def run_tests(pyb, tests, args, result_dir, num_threads=1):
- test_count = ThreadSafeCounter()
testcase_count = ThreadSafeCounter()
- passed_count = ThreadSafeCounter()
- failed_tests = ThreadSafeCounter([])
- skipped_tests = ThreadSafeCounter([])
+ test_results = ThreadSafeCounter([])
skip_tests = set()
skip_native = False
@@ -864,11 +878,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
test_basename = test_file.replace("..", "_").replace("./", "").replace("/", "_")
test_name = os.path.splitext(os.path.basename(test_file))[0]
- is_native = (
- test_name.startswith("native_")
- or test_name.startswith("viper_")
- or args.emit == "native"
- )
+ is_native = test_name.startswith("native_") or test_name.startswith("viper_")
is_endian = test_name.endswith("_endian")
is_int_big = test_name.startswith("int_big") or test_name.endswith("_intbig")
is_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray")
@@ -896,7 +906,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
if skip_it:
print("skip ", test_file)
- skipped_tests.append(test_name)
+ test_results.append((test_file, "skip", ""))
return
# Run the test on the MicroPython target.
@@ -911,7 +921,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
# start-up code (eg boot.py) when preparing to run the next test.
pyb.read_until(1, b"raw REPL; CTRL-B to exit\r\n")
print("skip ", test_file)
- skipped_tests.append(test_name)
+ test_results.append((test_file, "skip", ""))
+ return
+ elif output_mupy == b"SKIP-TOO-LARGE\n":
+ print("lrge ", test_file)
+ test_results.append((test_file, "skip", "too large"))
return
# Look at the output of the test to see if unittest was used.
@@ -994,7 +1008,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
# Print test summary, update counters, and save .exp/.out files if needed.
if test_passed:
print("pass ", test_file, extra_info)
- passed_count.increment()
+ test_results.append((test_file, "pass", ""))
rm_f(filename_expected)
rm_f(filename_mupy)
else:
@@ -1006,9 +1020,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
rm_f(filename_expected) # in case left over from previous failed run
with open(filename_mupy, "wb") as f:
f.write(output_mupy)
- failed_tests.append((test_name, test_file))
-
- test_count.increment()
+ test_results.append((test_file, "fail", ""))
# Print a note if this looks like it might have been a misfired unittest
if not uses_unittest and not test_passed:
@@ -1035,17 +1047,49 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
print(line)
sys.exit(1)
- print(
- "{} tests performed ({} individual testcases)".format(
- test_count.value, testcase_count.value
- )
+ # Return test results.
+ return test_results.value, testcase_count.value
+
+
+# Print a summary of the results and save them to a JSON file.
+# Returns True if everything succeeded, False otherwise.
+def create_test_report(args, test_results, testcase_count=None):
+ passed_tests = list(r for r in test_results if r[1] == "pass")
+ skipped_tests = list(r for r in test_results if r[1] == "skip" and r[2] != "too large")
+ skipped_tests_too_large = list(
+ r for r in test_results if r[1] == "skip" and r[2] == "too large"
)
- print("{} tests passed".format(passed_count.value))
+ failed_tests = list(r for r in test_results if r[1] == "fail")
+
+ num_tests_performed = len(passed_tests) + len(failed_tests)
+
+ testcase_count_info = ""
+ if testcase_count is not None:
+ testcase_count_info = " ({} individual testcases)".format(testcase_count)
+ print("{} tests performed{}".format(num_tests_performed, testcase_count_info))
+
+ print("{} tests passed".format(len(passed_tests)))
- skipped_tests = sorted(skipped_tests.value)
if len(skipped_tests) > 0:
- print("{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests)))
- failed_tests = sorted(failed_tests.value)
+ print(
+ "{} tests skipped: {}".format(
+ len(skipped_tests), " ".join(test[0] for test in skipped_tests)
+ )
+ )
+
+ if len(skipped_tests_too_large) > 0:
+ print(
+ "{} tests skipped because they are too large: {}".format(
+ len(skipped_tests_too_large), " ".join(test[0] for test in skipped_tests_too_large)
+ )
+ )
+
+ if len(failed_tests) > 0:
+ print(
+ "{} tests failed: {}".format(
+ len(failed_tests), " ".join(test[0] for test in failed_tests)
+ )
+ )
# Serialize regex added by append_filter.
def to_json(obj):
@@ -1053,23 +1097,22 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
return obj.pattern
return obj
- with open(os.path.join(result_dir, RESULTS_FILE), "w") as f:
+ with open(os.path.join(args.result_dir, RESULTS_FILE), "w") as f:
json.dump(
- {"args": vars(args), "failed_tests": [test[1] for test in failed_tests]},
+ {
+ # The arguments passed on the command-line.
+ "args": vars(args),
+ # A list of all results of the form [(test, result, reason), ...].
+ "results": list(test for test in test_results),
+ # A list of failed tests. This is deprecated, use the "results" above instead.
+ "failed_tests": [test[0] for test in failed_tests],
+ },
f,
default=to_json,
)
- if len(failed_tests) > 0:
- print(
- "{} tests failed: {}".format(
- len(failed_tests), " ".join(test[0] for test in failed_tests)
- )
- )
- return False
-
- # all tests succeeded
- return True
+ # Return True only if all tests succeeded.
+ return len(failed_tests) == 0
class append_filter(argparse.Action):
@@ -1238,7 +1281,7 @@ the last matching regex is used:
results_file = os.path.join(args.result_dir, RESULTS_FILE)
if os.path.exists(results_file):
with open(results_file, "r") as f:
- tests = json.load(f)["failed_tests"]
+ tests = list(test[0] for test in json.load(f)["results"] if test[1] == "fail")
else:
tests = []
elif len(args.files) == 0:
@@ -1316,7 +1359,8 @@ the last matching regex is used:
try:
os.makedirs(args.result_dir, exist_ok=True)
- res = run_tests(pyb, tests, args, args.result_dir, args.jobs)
+ test_results, testcase_count = run_tests(pyb, tests, args, args.result_dir, args.jobs)
+ res = create_test_report(args, test_results, testcase_count)
finally:
if pyb:
pyb.close()