diff options
Diffstat (limited to 'scripts/test')
-rwxr-xr-x | scripts/test/asm2wasm.py | 254 | ||||
-rwxr-xr-x | scripts/test/binaryenjs.py | 88 | ||||
-rwxr-xr-x | scripts/test/generate_lld_tests.py | 106 | ||||
-rwxr-xr-x | scripts/test/lld.py | 112 | ||||
-rw-r--r-- | scripts/test/shared.py | 553 | ||||
-rw-r--r-- | scripts/test/support.py | 280 | ||||
-rwxr-xr-x | scripts/test/wasm2js.py | 220 |
7 files changed, 807 insertions, 806 deletions
diff --git a/scripts/test/asm2wasm.py b/scripts/test/asm2wasm.py index 356d8c0f2..356a3d85c 100755 --- a/scripts/test/asm2wasm.py +++ b/scripts/test/asm2wasm.py @@ -25,137 +25,137 @@ from .shared import ( def test_asm2wasm(): - print('[ checking asm2wasm testcases... ]\n') - - for asm in tests: - if not asm.endswith('.asm.js'): - continue - for precise in [0, 1, 2]: - for opts in [1, 0]: - cmd = ASM2WASM + [os.path.join(options.binaryen_test, asm)] - if 'threads' in asm: - cmd += ['--enable-threads'] - wasm = asm.replace('.asm.js', '.fromasm') - if not precise: - cmd += ['--trap-mode=allow', '--ignore-implicit-traps'] - wasm += '.imprecise' - elif precise == 2: - cmd += ['--trap-mode=clamp'] - wasm += '.clamp' - if not opts: - wasm += '.no-opts' - if precise: - cmd += ['-O0'] # test that -O0 does nothing - else: - cmd += ['-O'] - if 'debugInfo' in asm: - cmd += ['-g'] - if 'noffi' in asm: - cmd += ['--no-legalize-javascript-ffi'] - if precise and opts: - # test mem init importing - open('a.mem', 'w').write(asm) - cmd += ['--mem-init=a.mem'] - if asm[0] == 'e': - cmd += ['--mem-base=1024'] - if '4GB' in asm: - cmd += ['--mem-max=4294967296'] - if 'i64' in asm or 'wasm-only' in asm or 'noffi' in asm: - cmd += ['--wasm-only'] - wasm = os.path.join(options.binaryen_test, wasm) - print('..', asm, wasm) - - def do_asm2wasm_test(): - actual = run_command(cmd) - - # verify output - if not os.path.exists(wasm): - fail_with_error('output .wast file %s does not exist' % wasm) - fail_if_not_identical_to_file(actual, wasm) - - binary_format_check(wasm, verify_final_result=False) - - # test both normally and with pass debug (so each inter-pass state - # is validated) - old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG') - try: - os.environ['BINARYEN_PASS_DEBUG'] = '1' - print("With BINARYEN_PASS_DEBUG=1:") - do_asm2wasm_test() - del os.environ['BINARYEN_PASS_DEBUG'] - print("With BINARYEN_PASS_DEBUG disabled:") - do_asm2wasm_test() - finally: - if old_pass_debug is not None: - os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug - else: - if 'BINARYEN_PASS_DEBUG' in os.environ: - del os.environ['BINARYEN_PASS_DEBUG'] - - # verify in wasm - if options.interpreter: - # remove imports, spec interpreter doesn't know what to do with them - subprocess.check_call(WASM_OPT + ['--remove-imports', wasm], - stdout=open('ztemp.wast', 'w'), - stderr=subprocess.PIPE) - proc = subprocess.Popen([options.interpreter, 'ztemp.wast'], - stderr=subprocess.PIPE) - out, err = proc.communicate() - if proc.returncode != 0: - try: # to parse the error - reported = err.split(':')[1] - start, end = reported.split('-') - start_line, start_col = map(int, start.split('.')) - lines = open('ztemp.wast').read().split('\n') - print() - print('=' * 80) - print(lines[start_line - 1]) - print((' ' * (start_col - 1)) + '^') - print((' ' * (start_col - 2)) + '/_\\') - print('=' * 80) - print(err) - except Exception: - # failed to pretty-print - fail_with_error('wasm interpreter error: ' + err) - fail_with_error('wasm interpreter error') - - # verify debug info - if 'debugInfo' in asm: - jsmap = 'a.wasm.map' - cmd += ['--source-map', jsmap, - '--source-map-url', 'http://example.org/' + jsmap, - '-o', 'a.wasm'] - run_command(cmd) - if not os.path.isfile(jsmap): - fail_with_error('Debug info map not created: %s' % jsmap) - with open(jsmap, 'rb') as actual: - fail_if_not_identical_to_file(actual.read(), wasm + '.map') - with open('a.wasm', 'rb') as binary: - url_section_name = bytes([16]) + bytes('sourceMappingURL', 'utf-8') - url = 'http://example.org/' + jsmap - assert len(url) < 256, 'name too long' - url_section_contents = bytes([len(url)]) + bytes(url, 'utf-8') - print(url_section_name) - binary_contents = binary.read() - if url_section_name not in binary_contents: - fail_with_error('source map url section not found in binary') - url_section_index = binary_contents.index(url_section_name) - if url_section_contents not in binary_contents[url_section_index:]: - fail_with_error('source map url not found in url section') + print('[ checking asm2wasm testcases... ]\n') + + for asm in tests: + if not asm.endswith('.asm.js'): + continue + for precise in [0, 1, 2]: + for opts in [1, 0]: + cmd = ASM2WASM + [os.path.join(options.binaryen_test, asm)] + if 'threads' in asm: + cmd += ['--enable-threads'] + wasm = asm.replace('.asm.js', '.fromasm') + if not precise: + cmd += ['--trap-mode=allow', '--ignore-implicit-traps'] + wasm += '.imprecise' + elif precise == 2: + cmd += ['--trap-mode=clamp'] + wasm += '.clamp' + if not opts: + wasm += '.no-opts' + if precise: + cmd += ['-O0'] # test that -O0 does nothing + else: + cmd += ['-O'] + if 'debugInfo' in asm: + cmd += ['-g'] + if 'noffi' in asm: + cmd += ['--no-legalize-javascript-ffi'] + if precise and opts: + # test mem init importing + open('a.mem', 'w').write(asm) + cmd += ['--mem-init=a.mem'] + if asm[0] == 'e': + cmd += ['--mem-base=1024'] + if '4GB' in asm: + cmd += ['--mem-max=4294967296'] + if 'i64' in asm or 'wasm-only' in asm or 'noffi' in asm: + cmd += ['--wasm-only'] + wasm = os.path.join(options.binaryen_test, wasm) + print('..', asm, wasm) + + def do_asm2wasm_test(): + actual = run_command(cmd) + + # verify output + if not os.path.exists(wasm): + fail_with_error('output .wast file %s does not exist' % wasm) + fail_if_not_identical_to_file(actual, wasm) + + binary_format_check(wasm, verify_final_result=False) + + # test both normally and with pass debug (so each inter-pass state + # is validated) + old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG') + try: + os.environ['BINARYEN_PASS_DEBUG'] = '1' + print("With BINARYEN_PASS_DEBUG=1:") + do_asm2wasm_test() + del os.environ['BINARYEN_PASS_DEBUG'] + print("With BINARYEN_PASS_DEBUG disabled:") + do_asm2wasm_test() + finally: + if old_pass_debug is not None: + os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug + else: + if 'BINARYEN_PASS_DEBUG' in os.environ: + del os.environ['BINARYEN_PASS_DEBUG'] + + # verify in wasm + if options.interpreter: + # remove imports, spec interpreter doesn't know what to do with them + subprocess.check_call(WASM_OPT + ['--remove-imports', wasm], + stdout=open('ztemp.wast', 'w'), + stderr=subprocess.PIPE) + proc = subprocess.Popen([options.interpreter, 'ztemp.wast'], + stderr=subprocess.PIPE) + out, err = proc.communicate() + if proc.returncode != 0: + try: # to parse the error + reported = err.split(':')[1] + start, end = reported.split('-') + start_line, start_col = map(int, start.split('.')) + lines = open('ztemp.wast').read().split('\n') + print() + print('=' * 80) + print(lines[start_line - 1]) + print((' ' * (start_col - 1)) + '^') + print((' ' * (start_col - 2)) + '/_\\') + print('=' * 80) + print(err) + except Exception: + # failed to pretty-print + fail_with_error('wasm interpreter error: ' + err) + fail_with_error('wasm interpreter error') + + # verify debug info + if 'debugInfo' in asm: + jsmap = 'a.wasm.map' + cmd += ['--source-map', jsmap, + '--source-map-url', 'http://example.org/' + jsmap, + '-o', 'a.wasm'] + run_command(cmd) + if not os.path.isfile(jsmap): + fail_with_error('Debug info map not created: %s' % jsmap) + with open(jsmap, 'rb') as actual: + fail_if_not_identical_to_file(actual.read(), wasm + '.map') + with open('a.wasm', 'rb') as binary: + url_section_name = bytes([16]) + bytes('sourceMappingURL', 'utf-8') + url = 'http://example.org/' + jsmap + assert len(url) < 256, 'name too long' + url_section_contents = bytes([len(url)]) + bytes(url, 'utf-8') + print(url_section_name) + binary_contents = binary.read() + if url_section_name not in binary_contents: + fail_with_error('source map url section not found in binary') + url_section_index = binary_contents.index(url_section_name) + if url_section_contents not in binary_contents[url_section_index:]: + fail_with_error('source map url not found in url section') def test_asm2wasm_binary(): - print('\n[ checking asm2wasm binary reading/writing... ]\n') + print('\n[ checking asm2wasm binary reading/writing... ]\n') - asmjs = os.path.join(options.binaryen_test, 'hello_world.asm.js') - delete_from_orbit('a.wasm') - delete_from_orbit('b.wast') - run_command(ASM2WASM + [asmjs, '-o', 'a.wasm']) - assert open('a.wasm', 'rb').read()[0] == 0, 'we emit binary by default' - run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S']) - assert open('b.wast', 'rb').read()[0] != 0, 'we emit text with -S' + asmjs = os.path.join(options.binaryen_test, 'hello_world.asm.js') + delete_from_orbit('a.wasm') + delete_from_orbit('b.wast') + run_command(ASM2WASM + [asmjs, '-o', 'a.wasm']) + assert open('a.wasm', 'rb').read()[0] == 0, 'we emit binary by default' + run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S']) + assert open('b.wast', 'rb').read()[0] != 0, 'we emit text with -S' if __name__ == '__main__': - test_asm2wasm() - test_asm2wasm_binary() + test_asm2wasm() + test_asm2wasm_binary() diff --git a/scripts/test/binaryenjs.py b/scripts/test/binaryenjs.py index f701f25c1..a102c2ae4 100755 --- a/scripts/test/binaryenjs.py +++ b/scripts/test/binaryenjs.py @@ -22,56 +22,56 @@ from .shared import BINARYEN_JS, MOZJS, NODEJS, options, fail def test_binaryen_js(): - if not (MOZJS or NODEJS): - print('no vm to run binaryen.js tests') - return + if not (MOZJS or NODEJS): + print('no vm to run binaryen.js tests') + return - node_has_wasm = NODEJS and node_has_webassembly(NODEJS) + node_has_wasm = NODEJS and node_has_webassembly(NODEJS) - if not os.path.exists(BINARYEN_JS): - print('no binaryen.js build to test') - return + if not os.path.exists(BINARYEN_JS): + print('no binaryen.js build to test') + return - print('\n[ checking binaryen.js testcases... ]\n') + print('\n[ checking binaryen.js testcases... ]\n') - for s in sorted(os.listdir(os.path.join(options.binaryen_test, 'binaryen.js'))): - if not s.endswith('.js'): - continue - print(s) - f = open('a.js', 'w') - # avoid stdout/stderr ordering issues in some js shells - use just stdout - f.write(''' - console.warn = function(x) { console.log(x) }; - ''') - binaryen_js = open(BINARYEN_JS).read() - f.write(binaryen_js) - if NODEJS: - f.write(node_test_glue()) - test_path = os.path.join(options.binaryen_test, 'binaryen.js', s) - test_src = open(test_path).read() - f.write(test_src) - f.close() + for s in sorted(os.listdir(os.path.join(options.binaryen_test, 'binaryen.js'))): + if not s.endswith('.js'): + continue + print(s) + f = open('a.js', 'w') + # avoid stdout/stderr ordering issues in some js shells - use just stdout + f.write(''' + console.warn = function(x) { console.log(x) }; + ''') + binaryen_js = open(BINARYEN_JS).read() + f.write(binaryen_js) + if NODEJS: + f.write(node_test_glue()) + test_path = os.path.join(options.binaryen_test, 'binaryen.js', s) + test_src = open(test_path).read() + f.write(test_src) + f.close() - def test(engine): - cmd = [engine, 'a.js'] - if 'fatal' not in s: - out = run_command(cmd, stderr=subprocess.STDOUT) - else: - # expect an error - the specific error code will depend on the vm - out = run_command(cmd, stderr=subprocess.STDOUT, expected_status=None) - expected = open(os.path.join(options.binaryen_test, 'binaryen.js', s + '.txt')).read() - if expected not in out: - fail(out, expected) + def test(engine): + cmd = [engine, 'a.js'] + if 'fatal' not in s: + out = run_command(cmd, stderr=subprocess.STDOUT) + else: + # expect an error - the specific error code will depend on the vm + out = run_command(cmd, stderr=subprocess.STDOUT, expected_status=None) + expected = open(os.path.join(options.binaryen_test, 'binaryen.js', s + '.txt')).read() + if expected not in out: + fail(out, expected) - # run in all possible shells - if MOZJS: - test(MOZJS) - if NODEJS: - if node_has_wasm or 'WebAssembly.' not in test_src: - test(NODEJS) - else: - print('Skipping ' + test_path + ' because WebAssembly might not be supported') + # run in all possible shells + if MOZJS: + test(MOZJS) + if NODEJS: + if node_has_wasm or 'WebAssembly.' not in test_src: + test(NODEJS) + else: + print('Skipping ' + test_path + ' because WebAssembly might not be supported') if __name__ == "__main__": - test_binaryen_js() + test_binaryen_js() diff --git a/scripts/test/generate_lld_tests.py b/scripts/test/generate_lld_tests.py index 9447fe973..b5cb6b140 100755 --- a/scripts/test/generate_lld_tests.py +++ b/scripts/test/generate_lld_tests.py @@ -23,69 +23,69 @@ import shared def files_with_extensions(path, extensions): - for file in sorted(os.listdir(path)): - ext = os.path.splitext(file)[1] - if ext in extensions: - yield file, ext + for file in sorted(os.listdir(path)): + ext = os.path.splitext(file)[1] + if ext in extensions: + yield file, ext def generate_wast_files(llvm_bin, emscripten_root): - print('\n[ building wast files from C sources... ]\n') + print('\n[ building wast files from C sources... ]\n') - lld_path = os.path.join(shared.options.binaryen_test, 'lld') - for src_file, ext in files_with_extensions(lld_path, ['.c', '.cpp']): - print('..', src_file) - obj_file = src_file.replace(ext, '.o') + lld_path = os.path.join(shared.options.binaryen_test, 'lld') + for src_file, ext in files_with_extensions(lld_path, ['.c', '.cpp']): + print('..', src_file) + obj_file = src_file.replace(ext, '.o') - src_path = os.path.join(lld_path, src_file) - obj_path = os.path.join(lld_path, obj_file) + src_path = os.path.join(lld_path, src_file) + obj_path = os.path.join(lld_path, obj_file) - wasm_file = src_file.replace(ext, '.wasm') - wast_file = src_file.replace(ext, '.wast') + wasm_file = src_file.replace(ext, '.wasm') + wast_file = src_file.replace(ext, '.wast') - obj_path = os.path.join(lld_path, obj_file) - wasm_path = os.path.join(lld_path, wasm_file) - wast_path = os.path.join(lld_path, wast_file) - is_shared = 'shared' in src_file + obj_path = os.path.join(lld_path, obj_file) + wasm_path = os.path.join(lld_path, wasm_file) + wast_path = os.path.join(lld_path, wast_file) + is_shared = 'shared' in src_file - compile_cmd = [ - os.path.join(llvm_bin, 'clang'), src_path, '-o', obj_path, - '--target=wasm32-unknown-unknown-wasm', - '-c', - '-nostdinc', - '-Xclang', '-nobuiltininc', - '-Xclang', '-nostdsysteminc', - '-Xclang', '-I%s/system/include' % emscripten_root, - '-O1', - ] + compile_cmd = [ + os.path.join(llvm_bin, 'clang'), src_path, '-o', obj_path, + '--target=wasm32-unknown-unknown-wasm', + '-c', + '-nostdinc', + '-Xclang', '-nobuiltininc', + '-Xclang', '-nostdsysteminc', + '-Xclang', '-I%s/system/include' % emscripten_root, + '-O1', + ] - link_cmd = [ - os.path.join(llvm_bin, 'wasm-ld'), '-flavor', 'wasm', - '-z', '-stack-size=1048576', - obj_path, '-o', wasm_path, - '--allow-undefined', - '--export', '__wasm_call_ctors', - '--global-base=568', - ] - if is_shared: - compile_cmd.append('-fPIC') - compile_cmd.append('-fvisibility=default') - link_cmd.append('-shared') - else: - link_cmd.append('--entry=main') + link_cmd = [ + os.path.join(llvm_bin, 'wasm-ld'), '-flavor', 'wasm', + '-z', '-stack-size=1048576', + obj_path, '-o', wasm_path, + '--allow-undefined', + '--export', '__wasm_call_ctors', + '--global-base=568', + ] + if is_shared: + compile_cmd.append('-fPIC') + compile_cmd.append('-fvisibility=default') + link_cmd.append('-shared') + else: + link_cmd.append('--entry=main') - try: - run_command(compile_cmd) - run_command(link_cmd) - run_command(shared.WASM_DIS + [wasm_path, '-o', wast_path]) - finally: - # Don't need the .o or .wasm files, don't leave them around - shared.delete_from_orbit(obj_path) - shared.delete_from_orbit(wasm_path) + try: + run_command(compile_cmd) + run_command(link_cmd) + run_command(shared.WASM_DIS + [wasm_path, '-o', wast_path]) + finally: + # Don't need the .o or .wasm files, don't leave them around + shared.delete_from_orbit(obj_path) + shared.delete_from_orbit(wasm_path) if __name__ == '__main__': - if len(shared.options.positional_args) != 2: - print('Usage: generate_lld_tests.py [llvm/bin/dir] [path/to/emscripten]') - sys.exit(1) - generate_wast_files(*shared.options.positional_args) + if len(shared.options.positional_args) != 2: + print('Usage: generate_lld_tests.py [llvm/bin/dir] [path/to/emscripten]') + sys.exit(1) + generate_wast_files(*shared.options.positional_args) diff --git a/scripts/test/lld.py b/scripts/test/lld.py index 1a553506f..4aafd9549 100755 --- a/scripts/test/lld.py +++ b/scripts/test/lld.py @@ -22,72 +22,72 @@ from .shared import ( def args_for_finalize(filename): - if 'safe_stack' in filename: - return ['--check-stack-overflow', '--global-base=568'] - elif 'shared' in filename: - return ['--side-module'] - else: - return ['--global-base=568'] + if 'safe_stack' in filename: + return ['--check-stack-overflow', '--global-base=568'] + elif 'shared' in filename: + return ['--side-module'] + else: + return ['--global-base=568'] def test_wasm_emscripten_finalize(): - print('\n[ checking wasm-emscripten-finalize testcases... ]\n') + print('\n[ checking wasm-emscripten-finalize testcases... ]\n') - for wast_path in files_with_pattern(options.binaryen_test, 'lld', '*.wast'): - print('..', wast_path) - is_passive = '.passive.' in wast_path - mem_file = wast_path + '.mem' - extension_arg_map = { - '.out': [], - } - if not is_passive: - extension_arg_map.update({ - '.mem.out': ['--separate-data-segments', mem_file], - }) - for ext, ext_args in extension_arg_map.items(): - expected_file = wast_path + ext - if ext != '.out' and not os.path.exists(expected_file): - continue + for wast_path in files_with_pattern(options.binaryen_test, 'lld', '*.wast'): + print('..', wast_path) + is_passive = '.passive.' in wast_path + mem_file = wast_path + '.mem' + extension_arg_map = { + '.out': [], + } + if not is_passive: + extension_arg_map.update({ + '.mem.out': ['--separate-data-segments', mem_file], + }) + for ext, ext_args in extension_arg_map.items(): + expected_file = wast_path + ext + if ext != '.out' and not os.path.exists(expected_file): + continue - cmd = WASM_EMSCRIPTEN_FINALIZE + [wast_path, '-S'] + ext_args - cmd += args_for_finalize(os.path.basename(wast_path)) - actual = run_command(cmd) + cmd = WASM_EMSCRIPTEN_FINALIZE + [wast_path, '-S'] + ext_args + cmd += args_for_finalize(os.path.basename(wast_path)) + actual = run_command(cmd) - if not os.path.exists(expected_file): - print(actual) - fail_with_error('output ' + expected_file + ' does not exist') - fail_if_not_identical_to_file(actual, expected_file) - if ext == '.mem.out': - with open(mem_file) as mf: - mem = mf.read() - fail_if_not_identical_to_file(mem, wast_path + '.mem.mem') - os.remove(mem_file) + if not os.path.exists(expected_file): + print(actual) + fail_with_error('output ' + expected_file + ' does not exist') + fail_if_not_identical_to_file(actual, expected_file) + if ext == '.mem.out': + with open(mem_file) as mf: + mem = mf.read() + fail_if_not_identical_to_file(mem, wast_path + '.mem.mem') + os.remove(mem_file) def update_lld_tests(): - print('\n[ updatring wasm-emscripten-finalize testcases... ]\n') + print('\n[ updatring wasm-emscripten-finalize testcases... ]\n') - for wast_path in files_with_pattern(options.binaryen_test, 'lld', '*.wast'): - print('..', wast_path) - is_passive = '.passive.' in wast_path - mem_file = wast_path + '.mem' - extension_arg_map = { - '.out': [], - } - if not is_passive: - extension_arg_map.update({ - '.mem.out': ['--separate-data-segments', mem_file + '.mem'], - }) - for ext, ext_args in extension_arg_map.items(): - out_path = wast_path + ext - if ext != '.out' and not os.path.exists(out_path): - continue - cmd = WASM_EMSCRIPTEN_FINALIZE + [wast_path, '-S'] + ext_args - cmd += args_for_finalize(os.path.basename(wast_path)) - actual = run_command(cmd) - with open(out_path, 'w') as o: - o.write(actual) + for wast_path in files_with_pattern(options.binaryen_test, 'lld', '*.wast'): + print('..', wast_path) + is_passive = '.passive.' in wast_path + mem_file = wast_path + '.mem' + extension_arg_map = { + '.out': [], + } + if not is_passive: + extension_arg_map.update({ + '.mem.out': ['--separate-data-segments', mem_file + '.mem'], + }) + for ext, ext_args in extension_arg_map.items(): + out_path = wast_path + ext + if ext != '.out' and not os.path.exists(out_path): + continue + cmd = WASM_EMSCRIPTEN_FINALIZE + [wast_path, '-S'] + ext_args + cmd += args_for_finalize(os.path.basename(wast_path)) + actual = run_command(cmd) + with open(out_path, 'w') as o: + o.write(actual) if __name__ == '__main__': - test_wasm_emscripten_finalize() + test_wasm_emscripten_finalize() diff --git a/scripts/test/shared.py b/scripts/test/shared.py index a712f5176..2440077a8 100644 --- a/scripts/test/shared.py +++ b/scripts/test/shared.py @@ -24,61 +24,61 @@ import sys def parse_args(args): - usage_str = ("usage: 'python check.py [options]'\n\n" - "Runs the Binaryen test suite.") - parser = argparse.ArgumentParser(description=usage_str) - parser.add_argument( - '--torture', dest='torture', action='store_true', default=True, - help='Chooses whether to run the torture testcases. Default: true.') - parser.add_argument( - '--no-torture', dest='torture', action='store_false', - help='Disables running the torture testcases.') - parser.add_argument( - '--abort-on-first-failure', dest='abort_on_first_failure', - action='store_true', default=True, - help=('Specifies whether to halt test suite execution on first test error.' - ' Default: true.')) - parser.add_argument( - '--no-abort-on-first-failure', dest='abort_on_first_failure', - action='store_false', - help=('If set, the whole test suite will run to completion independent of' - ' earlier errors.')) - - parser.add_argument( - '--interpreter', dest='interpreter', default='', - help='Specifies the wasm interpreter executable to run tests on.') - parser.add_argument( - '--binaryen-bin', dest='binaryen_bin', default='', - help=('Specifies a path to where the built Binaryen executables reside at.' - ' Default: bin/ of current directory (i.e. assume an in-tree build).' - ' If not specified, the environment variable BINARYEN_ROOT= can also' - ' be used to adjust this.')) - parser.add_argument( - '--binaryen-root', dest='binaryen_root', default='', - help=('Specifies a path to the root of the Binaryen repository tree.' - ' Default: the directory where this file check.py resides.')) - parser.add_argument( - '--valgrind', dest='valgrind', default='', - help=('Specifies a path to Valgrind tool, which will be used to validate' - ' execution if specified. (Pass --valgrind=valgrind to search in' - ' PATH)')) - parser.add_argument( - '--valgrind-full-leak-check', dest='valgrind_full_leak_check', - action='store_true', default=False, - help=('If specified, all unfreed (but still referenced) pointers at the' - ' end of execution are considered memory leaks. Default: disabled.')) - parser.add_argument( - '--spec-test', action='append', nargs='*', default=[], dest='spec_tests', - help='Names specific spec tests to run.') - parser.add_argument( - 'positional_args', metavar='TEST_SUITE', nargs='*', - help=('Names specific test suites to run. Use --list-suites to see a ' - 'list of all test suites')) - parser.add_argument( - '--list-suites', action='store_true', - help='List the test suites that can be run.') - - return parser.parse_args(args) + usage_str = ("usage: 'python check.py [options]'\n\n" + "Runs the Binaryen test suite.") + parser = argparse.ArgumentParser(description=usage_str) + parser.add_argument( + '--torture', dest='torture', action='store_true', default=True, + help='Chooses whether to run the torture testcases. Default: true.') + parser.add_argument( + '--no-torture', dest='torture', action='store_false', + help='Disables running the torture testcases.') + parser.add_argument( + '--abort-on-first-failure', dest='abort_on_first_failure', + action='store_true', default=True, + help=('Specifies whether to halt test suite execution on first test error.' + ' Default: true.')) + parser.add_argument( + '--no-abort-on-first-failure', dest='abort_on_first_failure', + action='store_false', + help=('If set, the whole test suite will run to completion independent of' + ' earlier errors.')) + + parser.add_argument( + '--interpreter', dest='interpreter', default='', + help='Specifies the wasm interpreter executable to run tests on.') + parser.add_argument( + '--binaryen-bin', dest='binaryen_bin', default='', + help=('Specifies a path to where the built Binaryen executables reside at.' + ' Default: bin/ of current directory (i.e. assume an in-tree build).' + ' If not specified, the environment variable BINARYEN_ROOT= can also' + ' be used to adjust this.')) + parser.add_argument( + '--binaryen-root', dest='binaryen_root', default='', + help=('Specifies a path to the root of the Binaryen repository tree.' + ' Default: the directory where this file check.py resides.')) + parser.add_argument( + '--valgrind', dest='valgrind', default='', + help=('Specifies a path to Valgrind tool, which will be used to validate' + ' execution if specified. (Pass --valgrind=valgrind to search in' + ' PATH)')) + parser.add_argument( + '--valgrind-full-leak-check', dest='valgrind_full_leak_check', + action='store_true', default=False, + help=('If specified, all unfreed (but still referenced) pointers at the' + ' end of execution are considered memory leaks. Default: disabled.')) + parser.add_argument( + '--spec-test', action='append', nargs='*', default=[], dest='spec_tests', + help='Names specific spec tests to run.') + parser.add_argument( + 'positional_args', metavar='TEST_SUITE', nargs='*', + help=('Names specific test suites to run. Use --list-suites to see a ' + 'list of all test suites')) + parser.add_argument( + '--list-suites', action='store_true', + help='List the test suites that can be run.') + + return parser.parse_args(args) options = parse_args(sys.argv[1:]) @@ -89,23 +89,23 @@ warnings = [] def warn(text): - global warnings - warnings.append(text) - print('warning:', text, file=sys.stderr) + global warnings + warnings.append(text) + print('warning:', text, file=sys.stderr) # setup # Locate Binaryen build artifacts directory (bin/ by default) if not options.binaryen_bin: - if os.environ.get('BINARYEN_ROOT'): - if os.path.isdir(os.path.join(os.environ.get('BINARYEN_ROOT'), 'bin')): - options.binaryen_bin = os.path.join( - os.environ.get('BINARYEN_ROOT'), 'bin') + if os.environ.get('BINARYEN_ROOT'): + if os.path.isdir(os.path.join(os.environ.get('BINARYEN_ROOT'), 'bin')): + options.binaryen_bin = os.path.join( + os.environ.get('BINARYEN_ROOT'), 'bin') + else: + options.binaryen_bin = os.environ.get('BINARYEN_ROOT') else: - options.binaryen_bin = os.environ.get('BINARYEN_ROOT') - else: - options.binaryen_bin = 'bin' + options.binaryen_bin = 'bin' options.binaryen_bin = os.path.normpath(os.path.abspath(options.binaryen_bin)) @@ -115,12 +115,12 @@ os.environ['BINARYEN_ROOT'] = os.path.dirname(options.binaryen_bin) wasm_dis_filenames = ['wasm-dis', 'wasm-dis.exe'] if not any(os.path.isfile(os.path.join(options.binaryen_bin, f)) for f in wasm_dis_filenames): - warn('Binaryen not found (or has not been successfully built to bin/ ?') + warn('Binaryen not found (or has not been successfully built to bin/ ?') # Locate Binaryen source directory if not specified. if not options.binaryen_root: - path_parts = os.path.abspath(__file__).split(os.path.sep) - options.binaryen_root = os.path.sep.join(path_parts[:-3]) + path_parts = os.path.abspath(__file__).split(os.path.sep) + options.binaryen_root = os.path.sep.join(path_parts[:-3]) options.binaryen_test = os.path.join(options.binaryen_root, 'test') @@ -128,30 +128,29 @@ options.binaryen_test = os.path.join(options.binaryen_root, 'test') # Finds the given executable 'program' in PATH. # Operates like the Unix tool 'which'. def which(program): - def is_exe(fpath): - return os.path.isfile(fpath) and os.access(fpath, os.X_OK) - fpath, fname = os.path.split(program) - if fpath: - if is_exe(program): - return program - else: - for path in os.environ["PATH"].split(os.pathsep): - path = path.strip('"') - exe_file = os.path.join(path, program) - if is_exe(exe_file): - return exe_file - if '.' not in fname: - if is_exe(exe_file + '.exe'): - return exe_file + '.exe' - if is_exe(exe_file + '.cmd'): - return exe_file + '.cmd' - if is_exe(exe_file + '.bat'): - return exe_file + '.bat' + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + if '.' not in fname: + if is_exe(exe_file + '.exe'): + return exe_file + '.exe' + if is_exe(exe_file + '.cmd'): + return exe_file + '.cmd' + if is_exe(exe_file + '.bat'): + return exe_file + '.bat' WATERFALL_BUILD_DIR = os.path.join(options.binaryen_test, 'wasm-install') -BIN_DIR = os.path.abspath(os.path.join( - WATERFALL_BUILD_DIR, 'wasm-install', 'bin')) +BIN_DIR = os.path.abspath(os.path.join(WATERFALL_BUILD_DIR, 'wasm-install', 'bin')) NATIVECC = (os.environ.get('CC') or which('mingw32-gcc') or which('gcc') or which('clang')) @@ -178,53 +177,53 @@ BINARYEN_JS = os.path.join(options.binaryen_root, 'out', 'binaryen.js') def wrap_with_valgrind(cmd): - # Exit code 97 is arbitrary, used to easily detect when an error occurs that - # is detected by Valgrind. - valgrind = [options.valgrind, '--quiet', '--error-exitcode=97'] - if options.valgrind_full_leak_check: - valgrind += ['--leak-check=full', '--show-leak-kinds=all'] - return valgrind + cmd + # Exit code 97 is arbitrary, used to easily detect when an error occurs that + # is detected by Valgrind. + valgrind = [options.valgrind, '--quiet', '--error-exitcode=97'] + if options.valgrind_full_leak_check: + valgrind += ['--leak-check=full', '--show-leak-kinds=all'] + return valgrind + cmd if options.valgrind: - WASM_OPT = wrap_with_valgrind(WASM_OPT) - WASM_AS = wrap_with_valgrind(WASM_AS) - WASM_DIS = wrap_with_valgrind(WASM_DIS) - ASM2WASM = wrap_with_valgrind(ASM2WASM) - WASM_SHELL = wrap_with_valgrind(WASM_SHELL) + WASM_OPT = wrap_with_valgrind(WASM_OPT) + WASM_AS = wrap_with_valgrind(WASM_AS) + WASM_DIS = wrap_with_valgrind(WASM_DIS) + ASM2WASM = wrap_with_valgrind(ASM2WASM) + WASM_SHELL = wrap_with_valgrind(WASM_SHELL) def in_binaryen(*args): - __rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - return os.path.join(__rootpath__, *args) + __rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + return os.path.join(__rootpath__, *args) os.environ['BINARYEN'] = in_binaryen() def get_platform(): - return {'linux': 'linux', - 'linux2': 'linux', - 'darwin': 'mac', - 'win32': 'windows', - 'cygwin': 'windows'}[sys.platform] + return {'linux': 'linux', + 'linux2': 'linux', + 'darwin': 'mac', + 'win32': 'windows', + 'cygwin': 'windows'}[sys.platform] def has_shell_timeout(): - return get_platform() != 'windows' and os.system('timeout 1s pwd') == 0 + return get_platform() != 'windows' and os.system('timeout 1s pwd') == 0 # Default options to pass to v8. These enable all features. V8_OPTS = [ - '--experimental-wasm-eh', - '--experimental-wasm-mv', - '--experimental-wasm-sat-f2i-conversions', - '--experimental-wasm-se', - '--experimental-wasm-threads', - '--experimental-wasm-simd', - '--experimental-wasm-anyref', - '--experimental-wasm-bulk-memory', - '--experimental-wasm-return-call' + '--experimental-wasm-eh', + '--experimental-wasm-mv', + '--experimental-wasm-sat-f2i-conversions', + '--experimental-wasm-se', + '--experimental-wasm-threads', + '--experimental-wasm-simd', + '--experimental-wasm-anyref', + '--experimental-wasm-bulk-memory', + '--experimental-wasm-return-call' ] has_vanilla_llvm = False @@ -232,232 +231,234 @@ has_vanilla_llvm = False # external tools try: - if NODEJS is not None: - subprocess.check_call( - [NODEJS, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if NODEJS is not None: + subprocess.check_call([NODEJS, '--version'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) except (OSError, subprocess.CalledProcessError): - NODEJS = None + NODEJS = None if NODEJS is None: - warn('no node found (did not check proper js form)') + warn('no node found (did not check proper js form)') try: - if MOZJS is not None: - subprocess.check_call( - [MOZJS, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if MOZJS is not None: + subprocess.check_call([MOZJS, '--version'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) except (OSError, subprocess.CalledProcessError): - MOZJS = None + MOZJS = None if MOZJS is None: - warn('no mozjs found (did not check native wasm support nor asm.js' - ' validation)') + warn('no mozjs found (did not check native wasm support nor asm.js' + ' validation)') try: - if EMCC is not None: - subprocess.check_call( - [EMCC, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if EMCC is not None: + subprocess.check_call([EMCC, '--version'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) except (OSError, subprocess.CalledProcessError): - EMCC = None + EMCC = None if EMCC is None: - warn('no emcc found (did not check non-vanilla emscripten/binaryen' - ' integration)') + warn('no emcc found (did not check non-vanilla emscripten/binaryen' + ' integration)') has_vanilla_emcc = False try: - subprocess.check_call( - [os.path.join(options.binaryen_test, 'emscripten', 'emcc'), '--version'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - has_vanilla_emcc = True + subprocess.check_call( + [os.path.join(options.binaryen_test, 'emscripten', 'emcc'), '--version'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + has_vanilla_emcc = True except (OSError, subprocess.CalledProcessError): - pass + pass # utilities # removes a file if it exists, using any and all ways of doing so def delete_from_orbit(filename): - try: - os.unlink(filename) - except OSError: - pass - if not os.path.exists(filename): - return - try: - shutil.rmtree(filename, ignore_errors=True) - except OSError: - pass - if not os.path.exists(filename): - return - try: - import stat - os.chmod(filename, os.stat(filename).st_mode | stat.S_IWRITE) - - def remove_readonly_and_try_again(func, path, exc_info): - if not (os.stat(path).st_mode & stat.S_IWRITE): - os.chmod(path, os.stat(path).st_mode | stat.S_IWRITE) - func(path) - else: - raise - shutil.rmtree(filename, onerror=remove_readonly_and_try_again) - except OSError: - pass + try: + os.unlink(filename) + except OSError: + pass + if not os.path.exists(filename): + return + try: + shutil.rmtree(filename, ignore_errors=True) + except OSError: + pass + if not os.path.exists(filename): + return + try: + import stat + os.chmod(filename, os.stat(filename).st_mode | stat.S_IWRITE) + + def remove_readonly_and_try_again(func, path, exc_info): + if not (os.stat(path).st_mode & stat.S_IWRITE): + os.chmod(path, os.stat(path).st_mode | stat.S_IWRITE) + func(path) + else: + raise + shutil.rmtree(filename, onerror=remove_readonly_and_try_again) + except OSError: + pass # This is a workaround for https://bugs.python.org/issue9400 class Py2CalledProcessError(subprocess.CalledProcessError): - def __init__(self, returncode, cmd, output=None, stderr=None): - super(Exception, self).__init__(returncode, cmd, output, stderr) - self.returncode = returncode - self.cmd = cmd - self.output = output - self.stderr = stderr + def __init__(self, returncode, cmd, output=None, stderr=None): + super(Exception, self).__init__(returncode, cmd, output, stderr) + self.returncode = returncode + self.cmd = cmd + self.output = output + self.stderr = stderr def run_process(cmd, check=True, input=None, capture_output=False, *args, **kw): - if input and type(input) == str: - input = bytes(input, 'utf-8') - if capture_output: - kw['stdout'] = subprocess.PIPE - kw['stderr'] = subprocess.PIPE - ret = subprocess.run(cmd, check=check, input=input, *args, **kw) - if ret.stdout is not None: - ret.stdout = ret.stdout.decode('utf-8') - if ret.stderr is not None: - ret.stderr = ret.stderr.decode('utf-8') - return ret + if input and type(input) == str: + input = bytes(input, 'utf-8') + if capture_output: + kw['stdout'] = subprocess.PIPE + kw['stderr'] = subprocess.PIPE + ret = subprocess.run(cmd, check=check, input=input, *args, **kw) + if ret.stdout is not None: + ret.stdout = ret.stdout.decode('utf-8') + if ret.stderr is not None: + ret.stderr = ret.stderr.decode('utf-8') + return ret def fail_with_error(msg): - global num_failures - try: - num_failures += 1 - raise Exception(msg) - except Exception as e: - print(str(e)) - if options.abort_on_first_failure: - raise + global num_failures + try: + num_failures += 1 + raise Exception(msg) + except Exception as e: + print(str(e)) + if options.abort_on_first_failure: + raise def fail(actual, expected, fromfile='expected'): - diff_lines = difflib.unified_diff( - expected.split('\n'), actual.split('\n'), - fromfile=fromfile, tofile='actual') - diff_str = ''.join([a.rstrip() + '\n' for a in diff_lines])[:] - fail_with_error("incorrect output, diff:\n\n%s" % diff_str) + diff_lines = difflib.unified_diff( + expected.split('\n'), actual.split('\n'), + fromfile=fromfile, tofile='actual') + diff_str = ''.join([a.rstrip() + '\n' for a in diff_lines])[:] + fail_with_error("incorrect output, diff:\n\n%s" % diff_str) def fail_if_not_identical(actual, expected, fromfile='expected'): - if expected != actual: - fail(actual, expected, fromfile=fromfile) + if expected != actual: + fail(actual, expected, fromfile=fromfile) def fail_if_not_contained(actual, expected): - if expected not in actual: - fail(actual, expected) + if expected not in actual: + fail(actual, expected) def fail_if_not_identical_to_file(actual, expected_file): - binary = expected_file.endswith(".wasm") or type(actual) == bytes - with open(expected_file, 'rb' if binary else 'r') as f: - fail_if_not_identical(actual, f.read(), fromfile=expected_file) + binary = expected_file.endswith(".wasm") or type(actual) == bytes + with open(expected_file, 'rb' if binary else 'r') as f: + fail_if_not_identical(actual, f.read(), fromfile=expected_file) if len(requested) == 0: - tests = sorted(os.listdir(os.path.join(options.binaryen_test))) + tests = sorted(os.listdir(os.path.join(options.binaryen_test))) else: - tests = requested[:] + tests = requested[:] if not options.interpreter: - warn('no interpreter provided (did not test spec interpreter validation)') + warn('no interpreter provided (did not test spec interpreter validation)') if not has_vanilla_emcc: - warn('no functional emcc submodule found') + warn('no functional emcc submodule found') # check utilities def validate_binary(wasm): - if V8: - cmd = [V8] + V8_OPTS + [in_binaryen('scripts', 'validation_shell.js'), '--', wasm] - print(' ', ' '.join(cmd)) - subprocess.check_call(cmd, stdout=subprocess.PIPE) - else: - print('(skipping v8 binary validation)') + if V8: + cmd = [V8] + V8_OPTS + [in_binaryen('scripts', 'validation_shell.js'), '--', wasm] + print(' ', ' '.join(cmd)) + subprocess.check_call(cmd, stdout=subprocess.PIPE) + else: + print('(skipping v8 binary validation)') def binary_format_check(wast, verify_final_result=True, wasm_as_args=['-g'], binary_suffix='.fromBinary', original_wast=None): - # checks we can convert the wast to binary and back - - print(' (binary format check)') - cmd = WASM_AS + [wast, '-o', 'a.wasm', '-all'] + wasm_as_args - print(' ', ' '.join(cmd)) - if os.path.exists('a.wasm'): - os.unlink('a.wasm') - subprocess.check_call(cmd, stdout=subprocess.PIPE) - assert os.path.exists('a.wasm') - - # make sure it is a valid wasm, using a real wasm VM - if os.path.basename(original_wast or wast) not in [ - 'atomics.wast', # https://bugs.chromium.org/p/v8/issues/detail?id=9425 - 'simd.wast', # https://bugs.chromium.org/p/v8/issues/detail?id=8460 - ]: - validate_binary('a.wasm') - - cmd = WASM_DIS + ['a.wasm', '-o', 'ab.wast'] - print(' ', ' '.join(cmd)) - if os.path.exists('ab.wast'): - os.unlink('ab.wast') - subprocess.check_call(cmd, stdout=subprocess.PIPE) - assert os.path.exists('ab.wast') - - # make sure it is a valid wast - cmd = WASM_OPT + ['ab.wast', '-all'] - print(' ', ' '.join(cmd)) - subprocess.check_call(cmd, stdout=subprocess.PIPE) - - if verify_final_result: - actual = open('ab.wast').read() - fail_if_not_identical_to_file(actual, wast + binary_suffix) - - return 'ab.wast' + # checks we can convert the wast to binary and back + + print(' (binary format check)') + cmd = WASM_AS + [wast, '-o', 'a.wasm', '-all'] + wasm_as_args + print(' ', ' '.join(cmd)) + if os.path.exists('a.wasm'): + os.unlink('a.wasm') + subprocess.check_call(cmd, stdout=subprocess.PIPE) + assert os.path.exists('a.wasm') + + # make sure it is a valid wasm, using a real wasm VM + if os.path.basename(original_wast or wast) not in [ + 'atomics.wast', # https://bugs.chromium.org/p/v8/issues/detail?id=9425 + 'simd.wast', # https://bugs.chromium.org/p/v8/issues/detail?id=8460 + ]: + validate_binary('a.wasm') + + cmd = WASM_DIS + ['a.wasm', '-o', 'ab.wast'] + print(' ', ' '.join(cmd)) + if os.path.exists('ab.wast'): + os.unlink('ab.wast') + subprocess.check_call(cmd, stdout=subprocess.PIPE) + assert os.path.exists('ab.wast') + + # make sure it is a valid wast + cmd = WASM_OPT + ['ab.wast', '-all'] + print(' ', ' '.join(cmd)) + subprocess.check_call(cmd, stdout=subprocess.PIPE) + + if verify_final_result: + actual = open('ab.wast').read() + fail_if_not_identical_to_file(actual, wast + binary_suffix) + + return 'ab.wast' def minify_check(wast, verify_final_result=True): - # checks we can parse minified output - - print(' (minify check)') - cmd = WASM_OPT + [wast, '--print-minified', '-all'] - print(' ', ' '.join(cmd)) - subprocess.check_call(cmd, stdout=open('a.wast', 'w'), stderr=subprocess.PIPE) - assert os.path.exists('a.wast') - subprocess.check_call( - WASM_OPT + ['a.wast', '--print-minified', '-all'], - stdout=open('b.wast', 'w'), stderr=subprocess.PIPE) - assert os.path.exists('b.wast') - if verify_final_result: - expected = open('a.wast').read() - actual = open('b.wast').read() - if actual != expected: - fail(actual, expected) - if os.path.exists('a.wast'): - os.unlink('a.wast') - if os.path.exists('b.wast'): - os.unlink('b.wast') + # checks we can parse minified output + + print(' (minify check)') + cmd = WASM_OPT + [wast, '--print-minified', '-all'] + print(' ', ' '.join(cmd)) + subprocess.check_call(cmd, stdout=open('a.wast', 'w'), stderr=subprocess.PIPE) + assert os.path.exists('a.wast') + subprocess.check_call(WASM_OPT + ['a.wast', '--print-minified', '-all'], + stdout=open('b.wast', 'w'), stderr=subprocess.PIPE) + assert os.path.exists('b.wast') + if verify_final_result: + expected = open('a.wast').read() + actual = open('b.wast').read() + if actual != expected: + fail(actual, expected) + if os.path.exists('a.wast'): + os.unlink('a.wast') + if os.path.exists('b.wast'): + os.unlink('b.wast') def files_with_pattern(*path_pattern): - return sorted(glob.glob(os.path.join(*path_pattern))) + return sorted(glob.glob(os.path.join(*path_pattern))) # run a check with BINARYEN_PASS_DEBUG set, to do full validation def with_pass_debug(check): - old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG') - try: - os.environ['BINARYEN_PASS_DEBUG'] = '1' - check() - finally: - if old_pass_debug is not None: - os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug - else: - if 'BINARYEN_PASS_DEBUG' in os.environ: - del os.environ['BINARYEN_PASS_DEBUG'] + old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG') + try: + os.environ['BINARYEN_PASS_DEBUG'] = '1' + check() + finally: + if old_pass_debug is not None: + os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug + else: + if 'BINARYEN_PASS_DEBUG' in os.environ: + del os.environ['BINARYEN_PASS_DEBUG'] diff --git a/scripts/test/support.py b/scripts/test/support.py index 90bc531eb..8f48a7af9 100644 --- a/scripts/test/support.py +++ b/scripts/test/support.py @@ -21,178 +21,178 @@ import tempfile def _open_archive(tarfile, tmp_dir): - with tempfile.TemporaryFile(mode='w+') as f: - try: - subprocess.check_call(['tar', '-xvf', tarfile], cwd=tmp_dir, stdout=f) - except Exception: - f.seek(0) - sys.stderr.write(f.read()) - raise - return os.listdir(tmp_dir) + with tempfile.TemporaryFile(mode='w+') as f: + try: + subprocess.check_call(['tar', '-xvf', tarfile], cwd=tmp_dir, stdout=f) + except Exception: + f.seek(0) + sys.stderr.write(f.read()) + raise + return os.listdir(tmp_dir) def _files_same(dir1, dir2, basenames): - diff = filecmp.cmpfiles(dir1, dir2, basenames) - return 0 == len(diff[1] + diff[2]) + diff = filecmp.cmpfiles(dir1, dir2, basenames) + return 0 == len(diff[1] + diff[2]) def _dirs_same(dir1, dir2, basenames): - for d in basenames: - left = os.path.join(dir1, d) - right = os.path.join(dir2, d) - if not (os.path.isdir(left) and os.path.isdir(right)): - return False - diff = filecmp.dircmp(right, right) - if 0 != len(diff.left_only + diff.right_only + diff.diff_files + - diff.common_funny + diff.funny_files): - return False - return True + for d in basenames: + left = os.path.join(dir1, d) + right = os.path.join(dir2, d) + if not (os.path.isdir(left) and os.path.isdir(right)): + return False + diff = filecmp.dircmp(right, right) + if 0 != len(diff.left_only + diff.right_only + diff.diff_files + + diff.common_funny + diff.funny_files): + return False + return True def _move_files(dirfrom, dirto, basenames): - for f in basenames: - from_file = os.path.join(dirfrom, f) - to_file = os.path.join(dirto, f) - if os.path.isfile(to_file): - os.path.remove(to_file) - shutil.move(from_file, to_file) + for f in basenames: + from_file = os.path.join(dirfrom, f) + to_file = os.path.join(dirto, f) + if os.path.isfile(to_file): + os.path.remove(to_file) + shutil.move(from_file, to_file) def _move_dirs(dirfrom, dirto, basenames): for d in basenames: - from_dir = os.path.join(dirfrom, d) - to_dir = os.path.join(dirto, d) - if os.path.isdir(to_dir): - shutil.rmtree(to_dir) - shutil.move(from_dir, to_dir) + from_dir = os.path.join(dirfrom, d) + to_dir = os.path.join(dirto, d) + if os.path.isdir(to_dir): + shutil.rmtree(to_dir) + shutil.move(from_dir, to_dir) def untar(tarfile, outdir): - """Returns True if untar content differs from pre-existing outdir content.""" - tmpdir = tempfile.mkdtemp() - try: - untared = _open_archive(tarfile, tmpdir) - files = [f for f in untared if os.path.isfile(os.path.join(tmpdir, f))] - dirs = [d for d in untared if os.path.isdir(os.path.join(tmpdir, d))] - assert len(files) + len(dirs) == len(untared), 'Only files and directories' - if _files_same(tmpdir, outdir, files) and _dirs_same(tmpdir, outdir, dirs): - # Nothing new or different in the tarfile. - return False - # Some or all of the files / directories are new. - _move_files(tmpdir, outdir, files) - _move_dirs(tmpdir, outdir, dirs) - return True - finally: - if os.path.isdir(tmpdir): - shutil.rmtree(tmpdir) + """Returns True if untar content differs from pre-existing outdir content.""" + tmpdir = tempfile.mkdtemp() + try: + untared = _open_archive(tarfile, tmpdir) + files = [f for f in untared if os.path.isfile(os.path.join(tmpdir, f))] + dirs = [d for d in untared if os.path.isdir(os.path.join(tmpdir, d))] + assert len(files) + len(dirs) == len(untared), 'Only files and directories' + if _files_same(tmpdir, outdir, files) and _dirs_same(tmpdir, outdir, dirs): + # Nothing new or different in the tarfile. + return False + # Some or all of the files / directories are new. + _move_files(tmpdir, outdir, files) + _move_dirs(tmpdir, outdir, dirs) + return True + finally: + if os.path.isdir(tmpdir): + shutil.rmtree(tmpdir) def split_wast(wastFile): - # if it's a binary, leave it as is, we can't split it - wast = None - if not wastFile.endswith('.wasm'): - try: - wast = open(wastFile, 'r').read() - except Exception: - pass - - if not wast: - return ((open(wastFile, 'rb').read(), []),) - - # .wast files can contain multiple modules, and assertions for each one. - # this splits out a wast into [(module, assertions), ..] - # we ignore module invalidity tests here. - ret = [] - - def to_end(j): - depth = 1 - while depth > 0 and j < len(wast): - if wast[j] == '"': - while 1: - j = wast.find('"', j + 1) - if wast[j - 1] == '\\': + # if it's a binary, leave it as is, we can't split it + wast = None + if not wastFile.endswith('.wasm'): + try: + wast = open(wastFile, 'r').read() + except Exception: + pass + + if not wast: + return ((open(wastFile, 'rb').read(), []),) + + # .wast files can contain multiple modules, and assertions for each one. + # this splits out a wast into [(module, assertions), ..] + # we ignore module invalidity tests here. + ret = [] + + def to_end(j): + depth = 1 + while depth > 0 and j < len(wast): + if wast[j] == '"': + while 1: + j = wast.find('"', j + 1) + if wast[j - 1] == '\\': + continue + break + assert j > 0 + elif wast[j] == '(': + depth += 1 + elif wast[j] == ')': + depth -= 1 + elif wast[j] == ';' and wast[j + 1] == ';': + j = wast.find('\n', j) + j += 1 + return j + + i = 0 + while i >= 0: + start = wast.find('(', i) + if start >= 0 and wast[start + 1] == ';': + # block comment + i = wast.find(';)', start + 2) + assert i > 0, wast[start:] + i += 2 + continue + skip = wast.find(';', i) + if skip >= 0 and skip < start and skip + 1 < len(wast): + if wast[skip + 1] == ';': + i = wast.find('\n', i) + 1 + continue + if start < 0: + break + i = to_end(start + 1) + chunk = wast[start:i] + if chunk.startswith('(module'): + ret += [(chunk, [])] + elif chunk.startswith('(assert_invalid'): continue - break - assert j > 0 - elif wast[j] == '(': - depth += 1 - elif wast[j] == ')': - depth -= 1 - elif wast[j] == ';' and wast[j + 1] == ';': - j = wast.find('\n', j) - j += 1 - return j - - i = 0 - while i >= 0: - start = wast.find('(', i) - if start >= 0 and wast[start + 1] == ';': - # block comment - i = wast.find(';)', start + 2) - assert i > 0, wast[start:] - i += 2 - continue - skip = wast.find(';', i) - if skip >= 0 and skip < start and skip + 1 < len(wast): - if wast[skip + 1] == ';': - i = wast.find('\n', i) + 1 - continue - if start < 0: - break - i = to_end(start + 1) - chunk = wast[start:i] - if chunk.startswith('(module'): - ret += [(chunk, [])] - elif chunk.startswith('(assert_invalid'): - continue - elif chunk.startswith(('(assert', '(invoke')): - ret[-1][1].append(chunk) - return ret + elif chunk.startswith(('(assert', '(invoke')): + ret[-1][1].append(chunk) + return ret # write a split wast from split_wast. the wast may be binary if the original # file was binary def write_wast(filename, wast, asserts=[]): - if type(wast) == bytes: - assert not asserts - with open(filename, 'wb') as o: - o.write(wast) - else: - with open(filename, 'w') as o: - o.write(wast + '\n'.join(asserts)) + if type(wast) == bytes: + assert not asserts + with open(filename, 'wb') as o: + o.write(wast) + else: + with open(filename, 'w') as o: + o.write(wast + '\n'.join(asserts)) def run_command(cmd, expected_status=0, stderr=None, expected_err=None, err_contains=False, err_ignore=None): - if expected_err is not None: - assert stderr == subprocess.PIPE or stderr is None,\ - "Can't redirect stderr if using expected_err" - stderr = subprocess.PIPE - print('executing: ', ' '.join(cmd)) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=stderr, universal_newlines=True) - out, err = proc.communicate() - code = proc.returncode - if expected_status is not None and code != expected_status: - raise Exception(('run_command failed (%s)' % code, out + str(err or ''))) - if expected_err is not None: - if err_ignore is not None: - err = "\n".join([line for line in err.split('\n') if err_ignore not in line]) - err_correct = expected_err in err if err_contains else expected_err == err - if not err_correct: - raise Exception(('run_command unexpected stderr', - "expected '%s', actual '%s'" % (expected_err, err))) - return out + if expected_err is not None: + assert stderr == subprocess.PIPE or stderr is None,\ + "Can't redirect stderr if using expected_err" + stderr = subprocess.PIPE + print('executing: ', ' '.join(cmd)) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=stderr, universal_newlines=True) + out, err = proc.communicate() + code = proc.returncode + if expected_status is not None and code != expected_status: + raise Exception(('run_command failed (%s)' % code, out + str(err or ''))) + if expected_err is not None: + if err_ignore is not None: + err = "\n".join([line for line in err.split('\n') if err_ignore not in line]) + err_correct = expected_err in err if err_contains else expected_err == err + if not err_correct: + raise Exception(('run_command unexpected stderr', + "expected '%s', actual '%s'" % (expected_err, err))) + return out def node_has_webassembly(cmd): - cmd = [cmd, '-e', 'process.stdout.write(typeof WebAssembly)'] - return run_command(cmd) == 'object' + cmd = [cmd, '-e', 'process.stdout.write(typeof WebAssembly)'] + return run_command(cmd) == 'object' def node_test_glue(): - # running concatenated files (a.js) in node interferes with module loading - # because the concatenated file expects a 'var Binaryen' but binaryen.js - # assigned to module.exports. this is correct behavior but tests then need - # a workaround: - return ('if (typeof module === "object" && typeof exports === "object")\n' - ' Binaryen = module.exports;\n') + # running concatenated files (a.js) in node interferes with module loading + # because the concatenated file expects a 'var Binaryen' but binaryen.js + # assigned to module.exports. this is correct behavior but tests then need + # a workaround: + return ('if (typeof module === "object" && typeof exports === "object")\n' + ' Binaryen = module.exports;\n') diff --git a/scripts/test/wasm2js.py b/scripts/test/wasm2js.py index 8c8f9a840..39ba946fe 100755 --- a/scripts/test/wasm2js.py +++ b/scripts/test/wasm2js.py @@ -36,155 +36,155 @@ wasm2js_blacklist = ['empty_imported_table.wast'] def test_wasm2js_output(): - for opt in (0, 1): - for wasm in tests + spec_tests + extra_wasm2js_tests: - if not wasm.endswith('.wast'): - continue - basename = os.path.basename(wasm) - if basename in wasm2js_blacklist: - continue + for opt in (0, 1): + for wasm in tests + spec_tests + extra_wasm2js_tests: + if not wasm.endswith('.wast'): + continue + basename = os.path.basename(wasm) + if basename in wasm2js_blacklist: + continue - asm = basename.replace('.wast', '.2asm.js') - expected_file = os.path.join(wasm2js_dir, asm) - if opt: - expected_file += '.opt' + asm = basename.replace('.wast', '.2asm.js') + expected_file = os.path.join(wasm2js_dir, asm) + if opt: + expected_file += '.opt' - if not os.path.exists(expected_file): - continue + if not os.path.exists(expected_file): + continue - print('..', wasm) + print('..', wasm) - t = os.path.join(options.binaryen_test, wasm) + t = os.path.join(options.binaryen_test, wasm) - all_out = [] + all_out = [] - for module, asserts in split_wast(t): - write_wast('split.wast', module, asserts) + for module, asserts in split_wast(t): + write_wast('split.wast', module, asserts) - cmd = WASM2JS + ['split.wast'] - if opt: - cmd += ['-O'] - if 'emscripten' in wasm: - cmd += ['--emscripten'] - out = run_command(cmd) - all_out.append(out) + cmd = WASM2JS + ['split.wast'] + if opt: + cmd += ['-O'] + if 'emscripten' in wasm: + cmd += ['--emscripten'] + out = run_command(cmd) + all_out.append(out) - if not NODEJS and not MOZJS: - print('No JS interpreters. Skipping spec tests.') - continue + if not NODEJS and not MOZJS: + print('No JS interpreters. Skipping spec tests.') + continue - open('a.2asm.mjs', 'w').write(out) + open('a.2asm.mjs', 'w').write(out) - cmd += ['--allow-asserts'] - out = run_command(cmd) - # also verify it passes pass-debug verifications - with_pass_debug(lambda: run_command(cmd)) + cmd += ['--allow-asserts'] + out = run_command(cmd) + # also verify it passes pass-debug verifications + with_pass_debug(lambda: run_command(cmd)) - open('a.2asm.asserts.mjs', 'w').write(out) + open('a.2asm.asserts.mjs', 'w').write(out) - # verify asm.js is valid js, note that we're using --experimental-modules - # to enable ESM syntax and we're also passing a custom loader to handle the - # `spectest` and `env` modules in our tests. - if NODEJS: - node = [NODEJS, '--experimental-modules', '--loader', './scripts/test/node-esm-loader.mjs'] - cmd = node[:] - cmd.append('a.2asm.mjs') - out = run_command(cmd) - fail_if_not_identical(out, '') - cmd = node[:] - cmd.append('a.2asm.asserts.mjs') - out = run_command(cmd, expected_err='', err_ignore='The ESM module loader is experimental') - fail_if_not_identical(out, '') + # verify asm.js is valid js, note that we're using --experimental-modules + # to enable ESM syntax and we're also passing a custom loader to handle the + # `spectest` and `env` modules in our tests. + if NODEJS: + node = [NODEJS, '--experimental-modules', '--loader', './scripts/test/node-esm-loader.mjs'] + cmd = node[:] + cmd.append('a.2asm.mjs') + out = run_command(cmd) + fail_if_not_identical(out, '') + cmd = node[:] + cmd.append('a.2asm.asserts.mjs') + out = run_command(cmd, expected_err='', err_ignore='The ESM module loader is experimental') + fail_if_not_identical(out, '') - fail_if_not_identical_to_file(''.join(all_out), expected_file) + fail_if_not_identical_to_file(''.join(all_out), expected_file) def test_asserts_output(): - for wasm in assert_tests: - print('..', wasm) + for wasm in assert_tests: + print('..', wasm) - asserts = os.path.basename(wasm).replace('.wast.asserts', '.asserts.js') - traps = os.path.basename(wasm).replace('.wast.asserts', '.traps.js') - asserts_expected_file = os.path.join(options.binaryen_test, asserts) - traps_expected_file = os.path.join(options.binaryen_test, traps) + asserts = os.path.basename(wasm).replace('.wast.asserts', '.asserts.js') + traps = os.path.basename(wasm).replace('.wast.asserts', '.traps.js') + asserts_expected_file = os.path.join(options.binaryen_test, asserts) + traps_expected_file = os.path.join(options.binaryen_test, traps) - wasm = os.path.join(wasm2js_dir, wasm) - cmd = WASM2JS + [wasm, '--allow-asserts'] - out = run_command(cmd) - fail_if_not_identical_to_file(out, asserts_expected_file) + wasm = os.path.join(wasm2js_dir, wasm) + cmd = WASM2JS + [wasm, '--allow-asserts'] + out = run_command(cmd) + fail_if_not_identical_to_file(out, asserts_expected_file) - cmd += ['--pedantic'] - out = run_command(cmd) - fail_if_not_identical_to_file(out, traps_expected_file) + cmd += ['--pedantic'] + out = run_command(cmd) + fail_if_not_identical_to_file(out, traps_expected_file) def test_wasm2js(): - print('\n[ checking wasm2js testcases... ]\n') - test_wasm2js_output() - test_asserts_output() + print('\n[ checking wasm2js testcases... ]\n') + test_wasm2js_output() + test_asserts_output() def update_wasm2js_tests(): - print('\n[ checking wasm2js ]\n') + print('\n[ checking wasm2js ]\n') - for opt in (0, 1): - for wasm in tests + spec_tests + extra_wasm2js_tests: - if not wasm.endswith('.wast'): - continue + for opt in (0, 1): + for wasm in tests + spec_tests + extra_wasm2js_tests: + if not wasm.endswith('.wast'): + continue - if os.path.basename(wasm) in wasm2js_blacklist: - continue + if os.path.basename(wasm) in wasm2js_blacklist: + continue - asm = os.path.basename(wasm).replace('.wast', '.2asm.js') - expected_file = os.path.join(wasm2js_dir, asm) - if opt: - expected_file += '.opt' + asm = os.path.basename(wasm).replace('.wast', '.2asm.js') + expected_file = os.path.join(wasm2js_dir, asm) + if opt: + expected_file += '.opt' - # we run wasm2js on tests and spec tests only if the output - # exists - only some work so far. the tests in extra are in - # the test/wasm2js dir and so are specific to wasm2js, and - # we run all of those. - if wasm not in extra_wasm2js_tests and not os.path.exists(expected_file): - continue + # we run wasm2js on tests and spec tests only if the output + # exists - only some work so far. the tests in extra are in + # the test/wasm2js dir and so are specific to wasm2js, and + # we run all of those. + if wasm not in extra_wasm2js_tests and not os.path.exists(expected_file): + continue - print('..', wasm) + print('..', wasm) - t = os.path.join(options.binaryen_test, wasm) + t = os.path.join(options.binaryen_test, wasm) - all_out = [] + all_out = [] - for module, asserts in split_wast(t): - write_wast('split.wast', module, asserts) + for module, asserts in split_wast(t): + write_wast('split.wast', module, asserts) - cmd = WASM2JS + ['split.wast'] - if opt: - cmd += ['-O'] - if 'emscripten' in wasm: - cmd += ['--emscripten'] - out = run_command(cmd) - all_out.append(out) + cmd = WASM2JS + ['split.wast'] + if opt: + cmd += ['-O'] + if 'emscripten' in wasm: + cmd += ['--emscripten'] + out = run_command(cmd) + all_out.append(out) - with open(expected_file, 'w') as o: - o.write(''.join(all_out)) + with open(expected_file, 'w') as o: + o.write(''.join(all_out)) - for wasm in assert_tests: - print('..', wasm) + for wasm in assert_tests: + print('..', wasm) - asserts = os.path.basename(wasm).replace('.wast.asserts', '.asserts.js') - traps = os.path.basename(wasm).replace('.wast.asserts', '.traps.js') - asserts_expected_file = os.path.join('test', asserts) - traps_expected_file = os.path.join('test', traps) + asserts = os.path.basename(wasm).replace('.wast.asserts', '.asserts.js') + traps = os.path.basename(wasm).replace('.wast.asserts', '.traps.js') + asserts_expected_file = os.path.join('test', asserts) + traps_expected_file = os.path.join('test', traps) - cmd = WASM2JS + [os.path.join(wasm2js_dir, wasm), '--allow-asserts'] - out = run_command(cmd) - with open(asserts_expected_file, 'w') as o: - o.write(out) + cmd = WASM2JS + [os.path.join(wasm2js_dir, wasm), '--allow-asserts'] + out = run_command(cmd) + with open(asserts_expected_file, 'w') as o: + o.write(out) - cmd += ['--pedantic'] - out = run_command(cmd) - with open(traps_expected_file, 'w') as o: - o.write(out) + cmd += ['--pedantic'] + out = run_command(cmd) + with open(traps_expected_file, 'w') as o: + o.write(out) if __name__ == "__main__": - test_wasm2js() + test_wasm2js() |