mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-15 04:32:21 +00:00
* fully refactor run_tests.py, make it extensible for the future * an ability to write @compilation_should_fail tests * an ability to launch run_tests.py for a single .fc file * keep run_tests.js in sync with run_tests.py * extract legacy_tests names/hashes to a separate file shared between legacy_tester.py and legacy_tester.js
138 lines
4.9 KiB
Python
138 lines
4.9 KiB
Python
# Usage: `legacy_tests.py` from current dir, providing some env (see getenv() calls).
|
|
# Unlike run_tests.py, it launches tests from legacy_tests/ folder (which are real-world contracts)
|
|
# and checks that code hashes are expected (that contracts are compiled exactly the same way).
|
|
# In other words, it doesn't execute TVM, it just compiles fift to acquire a contract hash.
|
|
# In the future, we may merge these tests with regular ones (when the testing framework becomes richer).
|
|
# Note, that there is also legacy_tester.js to test FunC compiled to WASM.
|
|
|
|
import os
|
|
import os.path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import shutil
|
|
|
|
add_pragmas = [] #["allow-post-modification", "compute-asm-ltr"];
|
|
|
|
|
|
def getenv(name, default=None):
|
|
if name in os.environ:
|
|
return os.environ[name]
|
|
if default is None:
|
|
print("Environment variable", name, "is not set", file=sys.stderr)
|
|
exit(1)
|
|
return default
|
|
|
|
|
|
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
|
|
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
|
|
FIFT_LIBS_FOLDER = getenv("FIFTPATH") # this env is needed for fift to work properly
|
|
TMP_DIR = tempfile.mkdtemp()
|
|
|
|
COMPILED_FIF = os.path.join(TMP_DIR, "compiled.fif")
|
|
RUNNER_FIF = os.path.join(TMP_DIR, "runner.fif")
|
|
|
|
TESTS_DIR = "legacy_tests"
|
|
|
|
|
|
def load_legacy_tests_list(jsonl_filename: str) -> list[tuple[str, int]]:
|
|
with open(jsonl_filename) as fd:
|
|
contents = fd.read()
|
|
results = re.findall('^\[\s*"(.*?)"\s*,\s*(.*?)\s*]', contents, re.MULTILINE)
|
|
return list(map(lambda line: (line[0], int(line[1])), results))
|
|
|
|
|
|
tests = load_legacy_tests_list('legacy_tests.jsonl')
|
|
|
|
|
|
class ExecutionError(Exception):
|
|
pass
|
|
|
|
def pre_process_func(f):
|
|
shutil.copyfile(f, f+"_backup")
|
|
with open(f, "r") as src:
|
|
sources = src.read()
|
|
with open(f, "w") as src:
|
|
for pragma in add_pragmas:
|
|
src.write("#pragma %s;\n"%pragma)
|
|
src.write(sources)
|
|
|
|
def post_process_func(f):
|
|
shutil.move(f+"_backup", f)
|
|
|
|
def compile_func(f):
|
|
res = None
|
|
try:
|
|
pre_process_func(f)
|
|
if "storage-provider.fc" in f :
|
|
# This contract requires building of storage-contract to include it as ref
|
|
with open(f, "r") as src:
|
|
sources = src.read()
|
|
COMPILED_ST_BOC = os.path.join(TMP_DIR, "storage-contract-code.boc")
|
|
sources = sources.replace("storage-contract-code.boc", COMPILED_ST_BOC)
|
|
with open(f, "w") as src:
|
|
src.write(sources)
|
|
COMPILED_ST_FIF = os.path.join(TMP_DIR, "storage-contract.fif")
|
|
COMPILED_ST_BOC = os.path.join(TMP_DIR, "storage-contract-code.boc")
|
|
COMPILED_BUILD_BOC = os.path.join(TMP_DIR, "build-boc.fif")
|
|
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_ST_FIF, "-SPA", f.replace("storage-provider.fc","storage-contract.fc")], capture_output=False, timeout=10)
|
|
with open(COMPILED_BUILD_BOC, "w") as scr:
|
|
scr.write("\"%s\" include boc>B \"%s\" B>file "%(COMPILED_ST_FIF, COMPILED_ST_BOC))
|
|
res = subprocess.run([FIFT_EXECUTABLE, COMPILED_BUILD_BOC ], capture_output=True, timeout=10)
|
|
|
|
|
|
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_FIF, "-SPA", f], capture_output=True, timeout=10)
|
|
except Exception as e:
|
|
post_process_func(f)
|
|
raise e
|
|
else:
|
|
post_process_func(f)
|
|
if res.returncode != 0:
|
|
raise ExecutionError(str(res.stderr, "utf-8"))
|
|
|
|
def run_runner():
|
|
res = subprocess.run([FIFT_EXECUTABLE, RUNNER_FIF], capture_output=True, timeout=10)
|
|
if res.returncode != 0:
|
|
raise ExecutionError(str(res.stderr, "utf-8"))
|
|
s = str(res.stdout, "utf-8")
|
|
s = s.strip()
|
|
return int(s)
|
|
|
|
def get_version():
|
|
res = subprocess.run([FUNC_EXECUTABLE, "-s"], capture_output=True, timeout=10)
|
|
if res.returncode != 0:
|
|
raise ExecutionError(str(res.stderr, "utf-8"))
|
|
s = str(res.stdout, "utf-8")
|
|
return s.strip()
|
|
|
|
success = 0
|
|
for ti, (filename_rel, code_hash) in enumerate(tests):
|
|
print("Running test %d/%d: %s" % (ti + 1, len(tests), filename_rel), file=sys.stderr)
|
|
try:
|
|
filename = os.path.join(TESTS_DIR, filename_rel)
|
|
compile_func(filename)
|
|
except ExecutionError as e:
|
|
print(file=sys.stderr)
|
|
print("Compilation error", file=sys.stderr)
|
|
print(e, file=sys.stderr)
|
|
exit(2)
|
|
|
|
with open(RUNNER_FIF, "w") as f:
|
|
print("\"%s\" include hash .s" % COMPILED_FIF , file=f)
|
|
|
|
try:
|
|
func_out = run_runner()
|
|
if func_out != code_hash:
|
|
raise ExecutionError("Error : expected '%d', found '%d'" % (code_hash, func_out))
|
|
success += 1
|
|
except ExecutionError as e:
|
|
print(e, file=sys.stderr)
|
|
print("Compiled:", file=sys.stderr)
|
|
with open(COMPILED_FIF, "r") as f:
|
|
print(f.read(), file=sys.stderr)
|
|
exit(2)
|
|
print(" OK ", file=sys.stderr)
|
|
|
|
print(get_version())
|
|
print("Done: Success %d, Error: %d"%(success, len(tests)-success), file=sys.stderr)
|