Pārlūkot izejas kodu

fix(runtest.py): A workaround to bypass errors that occur when deleting temporary files (#4093)

- Replace sys.exit with exceptions for better error handling in test assertions
- Update exception handling in compile_wast_to_wasm to catch all exceptions
- Improve error messages and logging
- Use `--ignore-whitespace` option for git apply in spec_test function
- Use raw string notation for regex patterns.  *The "SyntaxWarning: invalid escape sequence" in Python The warning has been upgraded to SyntaxWarning since Python 3.12, and it is expected to become a SyntaxError in future versions.*
- Add early return for non-loadable AOT compilation to prevent unnecessary assertions
- Redirect stderr to stdout in test_case for unified output
- Update `create_tmpfiles()`  to improve clarity and handling of temporary files
liang.he 9 mēneši atpakaļ
vecāks
revīzija
06ea960e76

+ 1 - 1
ci/coding_guidelines_check.py

@@ -145,7 +145,7 @@ def run_clang_format_diff(root: Path, commits: str) -> bool:
         found = False
         for summary in [x for x in diff_content if x.startswith("diff --git")]:
             # b/path/to/file -> path/to/file
-            with_invalid_format = re.split("\s+", summary)[-1][2:]
+            with_invalid_format = re.split(r"\s+", summary)[-1][2:]
             if not is_excluded(with_invalid_format):
                 print(f"--- {with_invalid_format} failed on code style checking.")
                 found = True

+ 1 - 1
test-tools/addr2line/addr2line.py

@@ -206,7 +206,7 @@ def get_line_info_from_function_addr_sourcemapping(
         if not line:
             continue
 
-        m = re.match("(.*):(\d+):(\d+)", line)
+        m = re.match(r"(.*):(\d+):(\d+)", line)
         if m:
             function_file, function_line, function_column = m.groups()
             continue

+ 4 - 2
tests/wamr-test-suites/spec-test-script/all.py

@@ -247,7 +247,7 @@ def test_case(
         CMD,
         bufsize=1,
         stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE,
+        stderr=subprocess.STDOUT,
         universal_newlines=True,
     ) as p:
         try:
@@ -285,7 +285,9 @@ def test_case(
         except subprocess.TimeoutExpired:
             print("failed with TimeoutExpired")
             raise Exception(case_name)
-
+        except Exception as e:
+            print(f"An unexpected error occurred: {e}")
+            raise e
 
 def test_suite(
     target,

+ 175 - 137
tests/wamr-test-suites/spec-test-script/runtest.py

@@ -7,6 +7,7 @@ import array
 import atexit
 import math
 import os
+import pathlib
 import re
 import shutil
 import struct
@@ -81,9 +82,8 @@ def log(data, end='\n'):
     print(data, end=end)
     sys.stdout.flush()
 
-def create_tmp_file(suffix: str) -> str:
-    with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp_file:
-        return tmp_file.name
+def create_tmp_file(prefix: str, suffix: str) -> str:
+    return tempfile.NamedTemporaryFile(prefix=prefix, suffix=suffix, delete=False).name
 
 # TODO: do we need to support '\n' too
 import platform
@@ -181,7 +181,16 @@ class Runner():
             # queue, so we keep it for non-windows platforms.
             [outs,_,_] = select([self.stdout], [], [], 1)
             if self.stdout in outs:
-                return True, self.stdout.read(1)
+                try:
+                    stdout_byte = self.stdout.read(1)
+                except ValueError:
+                    return True, None
+                except OSError:
+                    return True, None
+                except Exception as e:
+                    print("Exception: ", e)
+                    return False, None
+                return True, stdout_byte
             else:
                 return False, None
 
@@ -212,6 +221,8 @@ class Runner():
                     buf = self.buf[0:end-len(prompt)]
                     self.buf = self.buf[end:]
                     return buf
+
+        log("left read_to_prompt() because of timeout")
         return None
 
     def writeline(self, str):
@@ -249,14 +260,14 @@ def assert_prompt(runner, prompts, timeout, is_need_execute_result):
     header = runner.read_to_prompt(prompts, timeout=timeout)
     if not header and is_need_execute_result:
         log(" ---------- will terminate cause the case needs result while there is none inside of buf. ----------")
-        sys.exit(1)
+        raise Exception("get nothing from Runner")
     if not header == None:
         if header:
             log("Started with:\n%s" % header)
     else:
         log("Did not one of following prompt(s): %s" % repr(prompts))
         log("    Got      : %s" % repr(r.buf))
-        sys.exit(1)
+        raise Exception("Did not one of following prompt(s)")
 
 
 ### WebAssembly specific
@@ -551,7 +562,7 @@ def parse_assertion_value(val):
     if not val:
         return None, ""
 
-    splitted = re.split('\s+', val)
+    splitted = re.split(r'\s+', val)
     splitted = [s for s in splitted if s]
     type = splitted[0].split(".")[0]
     lane_type = splitted[1] if len(splitted) > 2 else ""
@@ -790,8 +801,8 @@ def test_assert(r, opts, mode, cmd, expected):
             return True
 
     ## 0x9:i32,-0x1:i32 -> ['0x9:i32', '-0x1:i32']
-    expected_list = re.split(',', expected)
-    out_list = re.split(',', out)
+    expected_list = re.split(r',', expected)
+    out_list = re.split(r',', out)
     if len(expected_list) != len(out_list):
         raise Exception("Failed:\n Results count incorrect:\n expected: '%s'\n  got: '%s'" % (expected, out))
     for i in range(len(expected_list)):
@@ -806,34 +817,34 @@ def test_assert_return(r, opts, form):
     n. to search a pattern like (assert_return (invoke $module_name function_name ... ) ...)
     """
     # params, return
-    m = re.search('^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S)
+    m = re.search(r'^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S)
     # judge if assert_return cmd includes the module name
-    n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S)
+    n = re.search(r'^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S)
 
     # print("assert_return with {}".format(form))
 
     if not m:
         # no params, return
-        m = re.search('^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S)
+        m = re.search(r'^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S)
     if not m:
         # params, no return
-        m = re.search('^\(assert_return\s+\(invoke\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S)
+        m = re.search(r'^\(assert_return\s+\(invoke\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S)
     if not m:
         # no params, no return
-        m = re.search('^\(assert_return\s+\(invoke\s+"([^"]*)"\s*()()\)\s*\)\s*$', form, re.S)
+        m = re.search(r'^\(assert_return\s+\(invoke\s+"([^"]*)"\s*()()\)\s*\)\s*$', form, re.S)
     if not m:
         # params, return
         if not n:
             # no params, return
-            n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S)
+            n = re.search(r'^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S)
         if not n:
             # params, no return
-            n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S)
+            n = re.search(r'^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S)
         if not n:
             # no params, no return
-            n = re.search('^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"*()()\)\s*\)\s*$', form, re.S)
+            n = re.search(r'^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"*()()\)\s*\)\s*$', form, re.S)
     if not m and not n:
-        if re.search('^\(assert_return\s+\(get.*\).*\)$', form, re.S):
+        if re.search(r'^\(assert_return\s+\(get.*\).*\)$', form, re.S):
             log("ignoring assert_return get")
             return
         else:
@@ -852,7 +863,7 @@ def test_assert_return(r, opts, form):
         if m.group(2) == '':
             args = []
         else:
-            #args = [re.split(' +', v)[1].replace('_', "") for v in re.split("\)\s*\(", m.group(2)[1:-1])]
+            #args = [re.split(r' +', v)[1].replace('_', "") for v in re.split(r"\)\s*\(", m.group(2)[1:-1])]
             # split arguments with ')spaces(', remove leading and tailing ) and (
             args_type_and_value = re.split(r'\)\s+\(', m.group(2)[1:-1])
             args_type_and_value = [s.replace('_', '') for s in args_type_and_value]
@@ -863,7 +874,7 @@ def test_assert_return(r, opts, form):
             for arg in args_type_and_value:
                 # remove leading and tailing spaces, it might confuse following assertions
                 arg = arg.strip()
-                splitted = re.split('\s+', arg)
+                splitted = re.split(r'\s+', arg)
                 splitted = [s for s in splitted if s]
 
                 if splitted[0] in ["i32.const", "i64.const"]:
@@ -881,7 +892,7 @@ def test_assert_return(r, opts, form):
                     numbers, _ = cast_v128_to_i64x2(splitted[2:], 'v128', splitted[1])
 
                     assert(len(numbers) == 2), "has to reform arguments into i64x2"
-                    args.append(f"{numbers[0]:#x}\{numbers[1]:#x}")
+                    args.append(f"{numbers[0]:#x}\\{numbers[1]:#x}")
                 elif "ref.null" == splitted[0]:
                     args.append("null")
                 elif "ref.extern" == splitted[0]:
@@ -896,7 +907,7 @@ def test_assert_return(r, opts, form):
         if m.group(3) == '':
             returns= []
         else:
-            returns = re.split("\)\s*\(", m.group(3)[1:-1])
+            returns = re.split(r"\)\s*\(", m.group(3)[1:-1])
         # processed numbers in strings
         if len(returns) == 1 and returns[0] in ["ref.array", "ref.struct", "ref.i31",
                                                 "ref.eq", "ref.any", "ref.extern",
@@ -921,8 +932,7 @@ def test_assert_return(r, opts, form):
             except:
                 _, exc, _ = sys.exc_info()
                 log("Run wamrc failed:\n  got: '%s'" % r.buf)
-                ret_code = 1
-                sys.exit(1)
+                raise Exception("Run wamrc failed 1")
         r = run_wasm_with_repl(module+".wasm", module+".aot" if test_aot else module, opts, r)
         # Wait for the initial prompt
         try:
@@ -941,23 +951,23 @@ def test_assert_return(r, opts, form):
             # convert (ref.null extern/func) into (ref.null null)
             n1 = n.group(3).replace("(ref.null extern)", "(ref.null null)")
             n1 = n1.replace("ref.null func)", "(ref.null null)")
-            args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n1[1:-1])]
+            args = [re.split(r' +', v)[1] for v in re.split(r"\)\s*\(", n1[1:-1])]
 
         _, expected = parse_assertion_value(n.group(4)[1:-1])
         test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected)
 
 def test_assert_trap(r, opts, form):
     # params
-    m = re.search('^\(assert_trap\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form)
+    m = re.search(r'^\(assert_trap\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form)
     # judge if assert_return cmd includes the module name
-    n = re.search('^\(assert_trap\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form, re.S)
+    n = re.search(r'^\(assert_trap\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form, re.S)
     if not m:
         # no params
-        m = re.search('^\(assert_trap\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form)
+        m = re.search(r'^\(assert_trap\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form)
     if not m:
         if not n:
             # no params
-            n = re.search('^\(assert_trap\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form, re.S)
+            n = re.search(r'^\(assert_trap\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form, re.S)
     if not m and not n:
         raise Exception("unparsed assert_trap: '%s'" % form)
 
@@ -969,7 +979,7 @@ def test_assert_trap(r, opts, form):
             # convert (ref.null extern/func) into (ref.null null)
             m1 = m.group(2).replace("(ref.null extern)", "(ref.null null)")
             m1 = m1.replace("ref.null func)", "(ref.null null)")
-            args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m1[1:-1])]
+            args = [re.split(r' +', v)[1] for v in re.split(r"\)\s*\(", m1[1:-1])]
 
         expected = "Exception: %s" % m.group(3)
         test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected)
@@ -987,8 +997,7 @@ def test_assert_trap(r, opts, form):
             except:
                 _, exc, _ = sys.exc_info()
                 log("Run wamrc failed:\n  got: '%s'" % r.buf)
-                ret_code = 1
-                sys.exit(1)
+                raise Exception("Run wamrc failed 2")
         r = run_wasm_with_repl(module+".wasm", module+".aot" if test_aot else module, opts, r)
         # Wait for the initial prompt
         try:
@@ -1002,23 +1011,23 @@ def test_assert_trap(r, opts, form):
         if n.group(3) == '':
             args = []
         else:
-            args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n.group(3)[1:-1])]
+            args = [re.split(r' +', v)[1] for v in re.split(r"\)\s*\(", n.group(3)[1:-1])]
         expected = "Exception: %s" % n.group(4)
         test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected)
 
 def test_assert_exhaustion(r,opts,form):
     # params
-    m = re.search('^\(assert_exhaustion\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form)
+    m = re.search(r'^\(assert_exhaustion\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form)
     if not m:
         # no params
-        m = re.search('^\(assert_exhaustion\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form)
+        m = re.search(r'^\(assert_exhaustion\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form)
     if not m:
         raise Exception("unparsed assert_exhaustion: '%s'" % form)
     func = m.group(1)
     if m.group(2) == '':
         args = []
     else:
-        args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
+        args = [re.split(r' +', v)[1] for v in re.split(r"\)\s*\(", m.group(2)[1:-1])]
     expected = "Exception: %s\n" % m.group(3)
     test_assert(r, opts, "exhaustion", "%s %s" % (func, " ".join(args)), expected)
 
@@ -1035,7 +1044,7 @@ def test_assert_wasmexception(r,opts,form):
     #         \)\s*
     #     \)\s*
     # $
-    m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*\)\s*$', form)
+    m = re.search(r'^\(assert_exception\s+\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*\)\s*$', form)
     if not m:
         # no params
 
@@ -1046,24 +1055,24 @@ def test_assert_wasmexception(r,opts,form):
         #           \)\s*
         #       \)\s*
         # $
-        m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s*()\)\s*\)\s*$', form)
+        m = re.search(r'^\(assert_exception\s+\(invoke\s+"([^"]+)"\s*()\)\s*\)\s*$', form)
     if not m:
         raise Exception("unparsed assert_exception: '%s'" % form)
     func = m.group(1) # function name
     if m.group(2) == '': # arguments
         args = []
     else:
-        args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
+        args = [re.split(r' +', v)[1] for v in re.split(r"\)\s*\(", m.group(2)[1:-1])]
 
     expected = "Exception: uncaught wasm exception\n"
     test_assert(r, opts, "wasmexception", "%s %s" % (func, " ".join(args)), expected)
 
 def do_invoke(r, opts, form):
     # params
-    m = re.search('^\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*$', form)
+    m = re.search(r'^\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*$', form)
     if not m:
         # no params
-        m = re.search('^\(invoke\s+"([^"]+)"\s*()\)\s*$', form)
+        m = re.search(r'^\(invoke\s+"([^"]+)"\s*()\)\s*$', form)
     if not m:
         raise Exception("unparsed invoke: '%s'" % form)
     func = m.group(1)
@@ -1074,7 +1083,7 @@ def do_invoke(r, opts, form):
     if m.group(2) == '':
         args = []
     else:
-        args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
+        args = [re.split(r' +', v)[1] for v in re.split(r"\)\s*\(", m.group(2)[1:-1])]
 
     log("Invoking %s(%s)" % (
         func, ", ".join([str(a) for a in args])))
@@ -1114,8 +1123,8 @@ def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
     log("Running: %s" % " ".join(cmd))
     try:
         subprocess.check_call(cmd)
-    except subprocess.CalledProcessError as e:
-        print(str(e))
+    except Exception as e:
+        print(e)
         return False
 
     return True
@@ -1238,13 +1247,17 @@ def run_wasm_with_repl(wasm_tempfile, aot_tempfile, opts, r):
 
     return r
 
-def create_tmpfiles(wast_name):
+def create_tmpfiles(file_name, test_aot, temp_file_repo):
     tempfiles = []
 
-    tempfiles.append(create_tmp_file(".wast"))
-    tempfiles.append(create_tmp_file(".wasm"))
+    tempfiles.append(create_tmp_file(file_name, ".wast"))
+    tempfiles.append(create_tmp_file(file_name, ".wasm"))
     if test_aot:
-        tempfiles.append(create_tmp_file(".aot"))
+        tempfiles.append(create_tmp_file(file_name, ".aot"))
+    else:
+        tempfiles.append(None)
+    
+    assert len(tempfiles) == 3, "tempfiles should have 3 elements"
 
     # add these temp file to temporal repo, will be deleted when finishing the test
     temp_file_repo.extend(tempfiles)
@@ -1263,6 +1276,9 @@ def test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile,
 
     if test_aot:
         r = compile_wasm_to_aot(wasm_tempfile, aot_tempfile, True, opts, r)
+        if not loadable:
+            return
+
         try:
             assert_prompt(r, ['Compile success'], opts.start_fail_timeout, True)
         except:
@@ -1275,8 +1291,7 @@ def test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile,
             else:
                 log("Run wamrc failed:\n  expected: '%s'\n  got: '%s'" % \
                     (expected, r.buf))
-                ret_code = 1
-                sys.exit(1)
+                raise Exception("Run wamrc failed 3")
 
     r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
 
@@ -1296,6 +1311,20 @@ def test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile,
                 raise Exception("Failed:\n  expected: '%s'\n  got: '%s'" % \
                                 (expected, r.buf))
 
+def recently_added_wasm(temp_file_repo):
+    for f in reversed(temp_file_repo):
+        if not f:
+            continue
+
+        assert os.path.exists(f), f"temp file {f} should exist"
+        
+        if os.path.getsize(f) == 0:
+            continue
+
+        if f.endswith(".wasm"):
+            return f
+
+
 if __name__ == "__main__":
     opts = parser.parse_args(sys.argv[1:])
     # print('Input param :',opts)
@@ -1314,16 +1343,10 @@ if __name__ == "__main__":
     else:
         SKIP_TESTS = C_SKIP_TESTS
 
-    wast_tempfile = create_tmp_file(".wast")
-    wasm_tempfile = create_tmp_file(".wasm")
-    if test_aot:
-        aot_tempfile = create_tmp_file(".aot")
-        # could be potientially compiled to aot
-        # with the future following call test_assert_xxx,
-        # add them to temp_file_repo now even if no actual following file,
-        # it will be simple ignore during final deletion if not exist
-        prefix = wasm_tempfile.split(".wasm")[0]
-        temp_file_repo.append(prefix + ".aot")
+    case_file = pathlib.Path(opts.test_file.name)
+    assert(case_file.exists()), f"Test file {case_file} doesn't exist"
+
+    tmpfile_stem = case_file.stem + "_"
 
     ret_code = 0
     try:
@@ -1335,22 +1358,26 @@ if __name__ == "__main__":
 
         for form in forms:
             # log("\n### Current Case is " + form + "\n")
+
+            wast_tempfile, wasm_tempfile, aot_tempfile = create_tmpfiles(
+                tmpfile_stem, test_aot, temp_file_repo)
+
             if ";;" == form[0:2]:
                 log(form)
             elif skip_test(form, SKIP_TESTS):
                 log("Skipping test: %s" % form[0:60])
-            elif re.match("^\(assert_trap\s+\(module", form):
+            elif re.match(r"^\(assert_trap\s+\(module", form):
                 test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
-            elif re.match("^\(assert_exhaustion\\b.*", form):
+            elif re.match(r"^\(assert_exhaustion\b.*", form):
                 test_assert_exhaustion(r, opts, form)
-            elif re.match("^\(assert_exception\\b.*", form):
+            elif re.match(r"^\(assert_exception\b.*", form):
                 test_assert_wasmexception(r, opts, form)
-            elif re.match("^\(assert_unlinkable\\b.*", form):
+            elif re.match(r"^\(assert_unlinkable\b.*", form):
                 test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r, False)
-            elif re.match("^\(assert_malformed\\b.*", form):
+            elif re.match(r"^\(assert_malformed\b.*", form):
                 # remove comments in wast
                 form,n = re.subn(";;.*\n", "", form)
-                m = re.match("^\(assert_malformed\s*\(module binary\s*(\".*\").*\)\s*\"(.*)\"\s*\)$", form, re.DOTALL)
+                m = re.match(r"^\(assert_malformed\s*\(module binary\s*(\".*\").*\)\s*\"(.*)\"\s*\)$", form, re.DOTALL)
 
                 if m:
                     # workaround: spec test changes error message to "malformed" while iwasm still use "invalid"
@@ -1359,7 +1386,7 @@ if __name__ == "__main__":
                     with open(wasm_tempfile, 'wb') as f:
                         s = m.group(1)
                         while s:
-                            res = re.match("[^\"]*\"([^\"]*)\"(.*)", s, re.DOTALL)
+                            res = re.match(r"[^\"]*\"([^\"]*)\"(.*)", s, re.DOTALL)
                             if IS_PY_3:
                                 context = res.group(1).replace("\\", "\\x").encode("latin1").decode("unicode-escape").encode("latin1")
                                 f.write(context)
@@ -1414,51 +1441,43 @@ if __name__ == "__main__":
                             else:
                                 log("Run wamrc failed:\n  expected: '%s'\n  got: '%s'" % \
                                     (error_msg, r.buf))
-                                ret_code = 1
-                                sys.exit(1)
+                                raise Exception("Run wamrc failed 4")
 
                     r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
 
-                elif re.match("^\(assert_malformed\s*\(module quote", form):
+                elif re.match(r"^\(assert_malformed\s*\(module quote", form):
                     log("ignoring assert_malformed module quote")
                 else:
                     log("unrecognized assert_malformed")
-            elif re.match("^\(assert_return[_a-z]*_nan\\b.*", form):
+            elif re.match(r"^\(assert_return[_a-z]*_nan\b.*", form):
                 log("ignoring assert_return_.*_nan")
                 pass
-            elif re.match(".*\(invoke\s+\$\\b.*", form):
+            elif re.match(r".*\(invoke\s+\$\b.*", form):
                 # invoke a particular named module's function
                 if form.startswith("(assert_return"):
                     test_assert_return(r,opts,form)
                 elif form.startswith("(assert_trap"):
                     test_assert_trap(r,opts,form)
-            elif re.match("^\(module\\b.*", form):
+            elif re.match(r"^\(module\b.*", form):
                 # if the module includes the particular name startswith $
-                m = re.search("^\(module\s+\$.\S+", form)
+                m = re.search(r"^\(module\s+\$.\S+", form)
                 if m:
                     # get module name
-                    module_name = re.split('\$', m.group(0).strip())[1]
+                    module_name = re.split(r'\$', m.group(0).strip())[1]
                     if module_name:
                         # create temporal files
-                        temp_files = create_tmpfiles(module_name)
+                        temp_files = create_tmpfiles(module_name, test_aot, temp_file_repo)
                         if not compile_wast_to_wasm(form, temp_files[0], temp_files[1], opts):
                             raise Exception("compile wast to wasm failed")
 
                         if test_aot:
                             r = compile_wasm_to_aot(temp_files[1], temp_files[2], True, opts, r)
-                            # could be potientially compiled to aot
-                            # with the future following call test_assert_xxx,
-                            # add them to temp_file_repo now even if no actual following file,
-                            # it will be simple ignore during final deletion if not exist
-                            prefix = temp_files[1].split(".wasm")[0]
-                            temp_file_repo.append(prefix + ".aot")
                             try:
                                 assert_prompt(r, ['Compile success'], opts.start_timeout, False)
                             except:
                                 _, exc, _ = sys.exc_info()
                                 log("Run wamrc failed:\n  got: '%s'" % r.buf)
-                                ret_code = 1
-                                sys.exit(1)
+                                raise Exception("Run wamrc failed 5")
                         temp_module_table[module_name] = temp_files[1]
                         r = run_wasm_with_repl(temp_files[1], temp_files[2] if test_aot else None, opts, r)
                 else:
@@ -1472,8 +1491,7 @@ if __name__ == "__main__":
                         except:
                             _, exc, _ = sys.exc_info()
                             log("Run wamrc failed:\n  got: '%s'" % r.buf)
-                            ret_code = 1
-                            sys.exit(1)
+                            raise Exception("Run wamrc failed 6")
 
                     r = run_wasm_with_repl(wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
 
@@ -1485,38 +1503,51 @@ if __name__ == "__main__":
                     raise Exception("Failed:\n  expected: '%s'\n  got: '%s'" % \
                                     (repr(exc), r.buf))
 
-            elif re.match("^\(assert_return\\b.*", form):
+            elif re.match(r"^\(assert_return\b.*", form):
                 assert(r), "iwasm repl runtime should be not null"
                 test_assert_return(r, opts, form)
-            elif re.match("^\(assert_trap\\b.*", form):
+            elif re.match(r"^\(assert_trap\b.*", form):
                 test_assert_trap(r, opts, form)
-            elif re.match("^\(invoke\\b.*", form):
+            elif re.match(r"^\(invoke\b.*", form):
                 assert(r), "iwasm repl runtime should be not null"
                 do_invoke(r, opts, form)
-            elif re.match("^\(assert_invalid\\b.*", form):
-                test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
-            elif re.match("^\(register\\b.*", form):
+            elif re.match(r"^\(assert_invalid\b.*", form):
+                # loading invalid module will raise an error directly, so shell prompt won't show here
+                test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r, False)
+            elif re.match(r"^\(register\b.*", form):
                 # get module's new name from the register cmd
-                name_new =re.split('\"',re.search('\".*\"',form).group(0))[1]
-                if name_new:
-                    new_module = os.path.join(tempfile.gettempdir(), name_new + ".wasm")
-                    shutil.copyfile(temp_module_table.get(name_new, wasm_tempfile), new_module)
-
-                    # add new_module copied from the old into temp_file_repo[]
-                    temp_file_repo.append(new_module)
-
-                    if test_aot:
-                        new_module_aot = os.path.join(tempfile.gettempdir(), name_new + ".aot")
-                        r = compile_wasm_to_aot(new_module, new_module_aot, True, opts, r)
-                        try:
-                            assert_prompt(r, ['Compile success'], opts.start_timeout, True)
-                        except:
-                            raise Exception("compile wasm to aot failed")
-                        # add aot module into temp_file_repo[]
-                        temp_file_repo.append(new_module_aot)
-                else:
+                name_new =re.split(r'\"',re.search(r'\".*\"',form).group(0))[1]
+                if not name_new:
                     # there is no name defined in register cmd
-                    raise Exception("can not find module name from the register")
+                    raise Exception(f"Not following register cmd pattern {form}")
+
+                # assumption
+                # - There exists a module in the form of (module $name).
+                # - The nearest module in the form of (module), without $name, is the candidate for registration.
+                recently_wasm = recently_added_wasm(temp_file_repo)
+                if not name_new in temp_module_table:
+                    print(temp_file_repo)
+                    print(f"Module {name_new} is not found in temp_module_table. use the nearest module {recently_wasm}")
+
+                for_registration = temp_module_table.get(name_new, recently_wasm)
+                assert os.path.exists(for_registration), f"module {for_registration} is not found"
+
+                new_module = os.path.join(tempfile.gettempdir(), name_new + ".wasm")
+                # for_registration(tmpfile) --copy-> name_new.wasm
+                shutil.copyfile(for_registration, new_module)
+
+                # add new_module copied from the old into temp_file_repo[]
+                temp_file_repo.append(new_module)
+
+                if test_aot:
+                    new_module_aot = os.path.join(tempfile.gettempdir(), name_new + ".aot")
+                    r = compile_wasm_to_aot(new_module, new_module_aot, True, opts, r)
+                    try:
+                        assert_prompt(r, ['Compile success'], opts.start_timeout, True)
+                    except:
+                        raise Exception("compile wasm to aot failed")
+                    # add aot module into temp_file_repo[]
+                    temp_file_repo.append(new_module_aot)
             else:
                 raise Exception("unrecognized form '%s...'" % form[0:40])
     except Exception as e:
@@ -1524,35 +1555,42 @@ if __name__ == "__main__":
         print("THE FINAL EXCEPTION IS {}".format(e))
         ret_code = 101
 
-        shutil.copyfile(wasm_tempfile, os.path.join(opts.log_dir, os.path.basename(wasm_tempfile)))
-
-        if opts.aot or opts.xip:
-            shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)))
-            if "indirect-mode" in str(e):
-                compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "object")
-                shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+'.o'))
-                subprocess.check_call(["llvm-objdump", "-r", aot_tempfile])
-            compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "ir")
-            shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+".ir"))
-
+        try:
+            shutil.copyfile(wasm_tempfile, os.path.join(opts.log_dir, os.path.basename(wasm_tempfile)))
+
+            if opts.aot or opts.xip:
+                shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)))
+                if "indirect-mode" in str(e):
+                    compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "object")
+                    shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+'.o'))
+                    subprocess.check_call(["llvm-objdump", "-r", aot_tempfile])
+                compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "ir")
+                shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+".ir"))
+        except Exception as e:
+            print("Failed to copy files to log directory: %s" % e)
+            ret_code = 102
     else:
         ret_code = 0
     finally:
-        if not opts.no_cleanup:
-            log("Removing tempfiles")
-            os.remove(wast_tempfile)
-            os.remove(wasm_tempfile)
-            if test_aot:
-                os.remove(aot_tempfile)
-
-            # remove the files under /tempfiles/ and copy of .wasm files
-            if temp_file_repo:
-                for t in temp_file_repo:
-                    if(len(str(t))!=0 and os.path.exists(t)):
-                        os.remove(t)
+        try:
+            if not opts.no_cleanup:
+                # remove the files under /tempfiles/ and copy of .wasm files
+                log(f"Removing {temp_file_repo}")
 
-            log("### End testing %s" % opts.test_file.name)
-        else:
-            log("Leaving tempfiles: %s" % ([wast_tempfile, wasm_tempfile]))
+                for t in temp_file_repo:
+                    # None and empty
+                    if not t:
+                        continue
 
+                    if os.path.exists(t):
+                        os.remove(t)
+            else:
+                log(f"Leaving {temp_file_repo}")
+            
+        except Exception as e:
+            print("Failed to remove tempfiles: %s" % e)
+            # ignore the exception
+            ret_code = 0
+
+        log(f"### End testing {opts.test_file.name} with {ret_code}")
         sys.exit(ret_code)

+ 13 - 13
tests/wamr-test-suites/test_wamr.sh

@@ -448,9 +448,9 @@ function spec_test()
 
         # May 31, 2012 [interpreter] implement atomic.wait and atomic.notify (#194)
         git reset --hard 09f2831349bf409187abb6f7868482a8079f2264
-        git apply ../../spec-test-script/thread_proposal_ignore_cases.patch || exit 1
-        git apply ../../spec-test-script/thread_proposal_fix_atomic_case.patch || exit 1
-        git apply ../../spec-test-script/thread_proposal_remove_memory64_flag_case.patch
+        git apply --ignore-whitespace ../../spec-test-script/thread_proposal_ignore_cases.patch || exit 1
+        git apply --ignore-whitespace ../../spec-test-script/thread_proposal_fix_atomic_case.patch || exit 1
+        git apply --ignore-whitespace ../../spec-test-script/thread_proposal_remove_memory64_flag_case.patch
     elif [ ${ENABLE_EH} == 1 ]; then
         echo "checkout exception-handling test cases"
 
@@ -459,7 +459,7 @@ function spec_test()
 
         # Jun 6, 2023 Merge branch 'upstream' into merge-upstream
         git reset --hard 51c721661b671bb7dc4b3a3acb9e079b49778d36
-        git apply ../../spec-test-script/exception_handling.patch || exit 1
+        git apply --ignore-whitespace ../../spec-test-script/exception_handling.patch || exit 1
     elif [[ ${ENABLE_GC} == 1 ]]; then
         echo "checkout spec for GC proposal"
 
@@ -469,12 +469,12 @@ function spec_test()
 
         #  Dec 9, 2024. Merge branch 'funcref'
         git reset --hard 756060f5816c7e2159f4817fbdee76cf52f9c923
-        git apply ../../spec-test-script/gc_ignore_cases.patch || exit 1
+        git apply --ignore-whitespace ../../spec-test-script/gc_ignore_cases.patch || exit 1
 
         if [[ ${ENABLE_QEMU} == 1 ]]; then
             # Decrease the recursive count for tail call cases as nuttx qemu's
             # native stack size is much smaller
-            git apply ../../spec-test-script/gc_nuttx_tail_call.patch || exit 1
+            git apply --ignore-whitespace ../../spec-test-script/gc_nuttx_tail_call.patch || exit 1
         fi
 
         # As of version 1.0.36, wabt is still unable to correctly handle the GC proposal.
@@ -497,7 +497,7 @@ function spec_test()
         git checkout 044d0d2e77bdcbe891f7e0b9dd2ac01d56435f0b -- test/core/elem.wast test/core/data.wast
         # Patch table64 extension
         git checkout 940398cd4823522a9b36bec4984be4b153dedb81 -- test/core/call_indirect.wast test/core/table.wast test/core/table_copy.wast test/core/table_copy_mixed.wast test/core/table_fill.wast test/core/table_get.wast test/core/table_grow.wast test/core/table_init.wast test/core/table_set.wast test/core/table_size.wast
-        git apply ../../spec-test-script/memory64_ignore_cases.patch || exit 1
+        git apply --ignore-whitespace ../../spec-test-script/memory64_ignore_cases.patch || exit 1
     elif [[ ${ENABLE_MULTI_MEMORY} == 1 ]]; then
         echo "checkout spec for multi memory proposal"
 
@@ -508,9 +508,9 @@ function spec_test()
         # Reset to commit: "Merge pull request #48 from backes/specify-memcpy-immediate-order"
         git reset --hard fbc99efd7a788db300aec3dd62a14577ec404f1b
         git checkout 044d0d2e77bdcbe891f7e0b9dd2ac01d56435f0b -- test/core/elem.wast
-        git apply ../../spec-test-script/multi_memory_ignore_cases.patch || exit 1
+        git apply --ignore-whitespace ../../spec-test-script/multi_memory_ignore_cases.patch || exit 1
         if [[ ${RUNNING_MODE} == "aot" ]]; then
-            git apply ../../spec-test-script/multi_module_aot_ignore_cases.patch || exit 1
+            git apply --ignore-whitespace ../../spec-test-script/multi_module_aot_ignore_cases.patch || exit 1
         fi
     else
         echo "checkout spec for default proposal"
@@ -520,15 +520,15 @@ function spec_test()
 
         # Dec 20, 2024. Use WPT version of test harness for HTML core test conversion (#1859)
         git reset --hard f3a0e06235d2d84bb0f3b5014da4370613886965
-        git apply ../../spec-test-script/ignore_cases.patch || exit 1
+        git apply --ignore-whitespace ../../spec-test-script/ignore_cases.patch || exit 1
         if [[ ${ENABLE_SIMD} == 1 ]]; then
-            git apply ../../spec-test-script/simd_ignore_cases.patch || exit 1
+            git apply --ignore-whitespace ../../spec-test-script/simd_ignore_cases.patch || exit 1
         fi
         if [[ ${ENABLE_MULTI_MODULE} == 1 ]]; then
-            git apply ../../spec-test-script/multi_module_ignore_cases.patch || exit 1
+            git apply --ignore-whitespace ../../spec-test-script/multi_module_ignore_cases.patch || exit 1
 
             if [[ ${RUNNING_MODE} == "aot" ]]; then
-                git apply ../../spec-test-script/multi_module_aot_ignore_cases.patch || exit 1
+                git apply --ignore-whitespace ../../spec-test-script/multi_module_aot_ignore_cases.patch || exit 1
             fi
         fi
     fi