From 0bd1eb80ff49dd0d4ad0c369a83a5aadea995944 Mon Sep 17 00:00:00 2001
From: Jim Mussared <jim.mussared@gmail.com>
Date: Mon, 19 Aug 2019 10:59:27 +1000
Subject: [PATCH] qemu-arm: Add testing of frozen native modules.

- Split 'qemu-arm' from 'unix' for generating tests.
- Add frozen module to the qemu-arm test build.
- Add test that reproduces the requirement to half-word align native
  function data.
---
 ports/qemu-arm/Makefile                       |  8 ++++++
 ports/qemu-arm/Makefile.test                  |  7 +++--
 .../test-frzmpy/native_frozen_align.py        | 13 +++++++++
 tests/qemu-arm/native_test.py                 |  5 ++++
 tests/qemu-arm/native_test.py.exp             |  3 +++
 tests/run-tests                               | 27 ++++++++++++-------
 tools/tinytest-codegen.py                     |  4 ++-
 7 files changed, 54 insertions(+), 13 deletions(-)
 create mode 100644 ports/qemu-arm/test-frzmpy/native_frozen_align.py
 create mode 100644 tests/qemu-arm/native_test.py
 create mode 100644 tests/qemu-arm/native_test.py.exp

diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile
index 03a8afe77..c730c8297 100644
--- a/ports/qemu-arm/Makefile
+++ b/ports/qemu-arm/Makefile
@@ -114,6 +114,14 @@ OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST)
 # List of sources for qstr extraction
 SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C)
 
+ifneq ($(FROZEN_MPY_DIR),)
+# To use frozen bytecode, put your .py files in a subdirectory (eg frozen/) and
+# then invoke make with FROZEN_MPY_DIR=frozen (be sure to build from scratch).
+CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool
+CFLAGS += -DMICROPY_MODULE_FROZEN_MPY
+MPY_CROSS_FLAGS += -march=armv7m
+endif
+
 all: run
 
 run: $(BUILD)/firmware.elf
diff --git a/ports/qemu-arm/Makefile.test b/ports/qemu-arm/Makefile.test
index 347c2fefd..32ec95a4f 100644
--- a/ports/qemu-arm/Makefile.test
+++ b/ports/qemu-arm/Makefile.test
@@ -1,5 +1,7 @@
 LIB_SRC_C = lib/upytesthelper/upytesthelper.c
 
+FROZEN_MPY_DIR ?= test-frzmpy
+
 include Makefile
 
 CFLAGS += -DTEST
@@ -8,7 +10,7 @@ CFLAGS += -DTEST
 
 $(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h
 $(BUILD)/genhdr/tests.h:
-	(cd $(TOP)/tests; ./run-tests --write-exp)
+	(cd $(TOP)/tests; ./run-tests --target=qemu-arm --write-exp)
 	$(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py) > $@
 
 $(BUILD)/tinytest.o:
@@ -18,7 +20,8 @@ $(BUILD)/firmware-test.elf: $(OBJ_COMMON) $(OBJ_TEST)
 	$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
 	$(Q)$(SIZE) $@
 
+# Note: Using timeout(1) to handle cases where qemu hangs (e.g. this can happen with alignment errors).
 test: $(BUILD)/firmware-test.elf
-	qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out
+	timeout --foreground -k 5s 30s qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out
 	$(Q)tail -n2 $(BUILD)/console.out
 	$(Q)tail -n1 $(BUILD)/console.out | grep -q "status: 0"
diff --git a/ports/qemu-arm/test-frzmpy/native_frozen_align.py b/ports/qemu-arm/test-frzmpy/native_frozen_align.py
new file mode 100644
index 000000000..5c5c0e8d2
--- /dev/null
+++ b/ports/qemu-arm/test-frzmpy/native_frozen_align.py
@@ -0,0 +1,13 @@
+import micropython
+
+@micropython.native
+def native_x(x):
+    print(x + 1)
+
+@micropython.native
+def native_y(x):
+    print(x + 1)
+
+@micropython.native
+def native_z(x):
+    print(x + 1)
diff --git a/tests/qemu-arm/native_test.py b/tests/qemu-arm/native_test.py
new file mode 100644
index 000000000..0b58433d9
--- /dev/null
+++ b/tests/qemu-arm/native_test.py
@@ -0,0 +1,5 @@
+import native_frozen_align
+
+native_frozen_align.native_x(1)
+native_frozen_align.native_y(2)
+native_frozen_align.native_z(3)
diff --git a/tests/qemu-arm/native_test.py.exp b/tests/qemu-arm/native_test.py.exp
new file mode 100644
index 000000000..dcf37cd5e
--- /dev/null
+++ b/tests/qemu-arm/native_test.py.exp
@@ -0,0 +1,3 @@
+2
+3
+4
diff --git a/tests/run-tests b/tests/run-tests
index 9f74c1cfc..c45d2787e 100755
--- a/tests/run-tests
+++ b/tests/run-tests
@@ -349,6 +349,8 @@ def run_tests(pyb, tests, args, base_path="."):
             for t in tests:
                 if t.startswith('basics/io_'):
                     skip_tests.add(t)
+        elif args.target == 'qemu-arm':
+            skip_tests.add('misc/print_exception.py')       # requires sys stdfiles
 
     # Some tests are known to fail on 64-bit machines
     if pyb is None and platform.architecture()[0] == '64bit':
@@ -527,8 +529,9 @@ the last matching regex is used:
     cmd_parser.add_argument('files', nargs='*', help='input test files')
     args = cmd_parser.parse_args()
 
+    LOCAL_TARGETS = ('unix', 'qemu-arm',)
     EXTERNAL_TARGETS = ('pyboard', 'wipy', 'esp8266', 'esp32', 'minimal', 'nrf')
-    if args.target == 'unix' or args.list_tests:
+    if args.target in LOCAL_TARGETS or args.list_tests:
         pyb = None
     elif args.target in EXTERNAL_TARGETS:
         global pyboard
@@ -537,24 +540,28 @@ the last matching regex is used:
         pyb = pyboard.Pyboard(args.device, args.baudrate, args.user, args.password)
         pyb.enter_raw_repl()
     else:
-        raise ValueError('target must be either %s or unix' % ", ".join(EXTERNAL_TARGETS))
+        raise ValueError('target must be one of %s' % ", ".join(LOCAL_TARGETS + EXTERNAL_TARGETS))
 
     if len(args.files) == 0:
         if args.test_dirs is None:
+            test_dirs = ('basics', 'micropython', 'misc', 'extmod',)
             if args.target == 'pyboard':
                 # run pyboard tests
-                test_dirs = ('basics', 'micropython', 'float', 'misc', 'stress', 'extmod', 'pyb', 'pybnative', 'inlineasm')
+                test_dirs += ('float', 'stress', 'pyb', 'pybnative', 'inlineasm')
             elif args.target in ('esp8266', 'esp32', 'minimal', 'nrf'):
-                test_dirs = ('basics', 'micropython', 'float', 'misc', 'extmod')
+                test_dirs += ('float',)
             elif args.target == 'wipy':
                 # run WiPy tests
-                test_dirs = ('basics', 'micropython', 'misc', 'extmod', 'wipy')
-            else:
+                test_dirs += ('wipy',)
+            elif args.target == 'unix':
                 # run PC tests
-                test_dirs = (
-                    'basics', 'micropython', 'float', 'import', 'io', 'misc',
-                    'stress', 'unicode', 'extmod', 'unix', 'cmdline',
-                )
+                test_dirs += ('float', 'import', 'io', 'stress', 'unicode', 'unix', 'cmdline',)
+            elif args.target == 'qemu-arm':
+                if not args.write_exp:
+                    raise ValueError('--target=qemu-arm must be used with --write-exp')
+                # Generate expected output files for qemu run.
+                # This list should match the test_dirs tuple in tinytest-codegen.py.
+                test_dirs += ('float', 'inlineasm', 'qemu-arm',)
         else:
             # run tests from these directories
             test_dirs = args.test_dirs
diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py
index ad3b3bbec..bdace1c8b 100755
--- a/tools/tinytest-codegen.py
+++ b/tools/tinytest-codegen.py
@@ -54,7 +54,7 @@ testgroup_member = (
 
 ## XXX: may be we could have `--without <groups>` argument...
 # currently these tests are selected because they pass on qemu-arm
-test_dirs = ('basics', 'micropython', 'float', 'extmod', 'inlineasm') # 'import', 'io', 'misc')
+test_dirs = ('basics', 'micropython', 'misc', 'extmod', 'float', 'inlineasm', 'qemu-arm',) # 'import', 'io',)
 exclude_tests = (
     # pattern matching in .exp
     'basics/bytes_compare3.py',
@@ -81,6 +81,8 @@ exclude_tests = (
     'micropython/heapalloc_traceback.py',
     # pattern matching in .exp
     'micropython/meminfo.py',
+    # needs sys stdfiles
+    'misc/print_exception.py',
 )
 
 output = []
-- 
GitLab