diff options
Diffstat (limited to 'check.py')
-rwxr-xr-x | check.py | 261 |
1 files changed, 212 insertions, 49 deletions
@@ -1,6 +1,6 @@ #!/usr/bin/env python -import os, sys, subprocess, difflib, json +import os, sys, subprocess, difflib, json, time interpreter = None requested = [] @@ -13,9 +13,34 @@ for arg in sys.argv[1:]: else: requested.append(arg) +# external tools + +has_node = False +try: + subprocess.check_call(['nodejs', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + has_node = True +except: + pass + +has_mozjs = False +try: + subprocess.check_call(['mozjs', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + has_mozjs = True +except: + pass + +has_emcc = False +try: + subprocess.check_call(['emcc', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + has_emcc = True +except: + pass + +# utilities + def fail(actual, expected): raise Exception("incorrect output, diff:\n\n%s" % ( - ''.join([a.rstrip()+'\n' for a in difflib.unified_diff(expected.split('\n'), actual.split('\n'), fromfile='expected', tofile='actual')]) + ''.join([a.rstrip()+'\n' for a in difflib.unified_diff(expected.split('\n'), actual.split('\n'), fromfile='expected', tofile='actual')])[-1000:] )) def fail_if_not_identical(actual, expected): @@ -26,20 +51,30 @@ def fail_if_not_contained(actual, expected): if expected not in actual: fail(actual, expected) -if not interpreter: - print '[ no wasm interpreter provided, you should pass one as --interpreter=path/to/interpreter ]' - -print '[ checking asm2wasm testcases... ]\n' - if len(requested) == 0: tests = sorted(os.listdir('test')) else: tests = requested[:] +if not interpreter: + print 'warning: no interpreter provided (not testing spec interpreter validation)' + time.sleep(0.5) +if not has_node: + print 'warning: no node found (not checking proper js form)' + time.sleep(0.5) +if not has_mozjs: + print 'warning: no mozjs found (not checking asm.js validation)' + time.sleep(0.5) +if not has_emcc: + print 'warning: no emcc found (not checking emscripten/binaryen integration)' + time.sleep(0.5) + +print '[ checking asm2wasm testcases... ]\n' + for asm in tests: if asm.endswith('.asm.js'): - print '..', asm - wasm = asm.replace('.asm.js', '.wast') + wasm = asm.replace('.asm.js', '.fromasm') + print '..', asm, wasm actual, err = subprocess.Popen([os.path.join('bin', 'asm2wasm'), os.path.join('test', asm)], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() assert err == '', 'bad err:' + err @@ -53,25 +88,62 @@ for asm in tests: # verify in wasm if interpreter: - proc = subprocess.Popen([interpreter, os.path.join('test', wasm)], stderr=subprocess.PIPE) + # remove imports, spec interpreter doesn't know what to do with them + subprocess.check_call([os.path.join('bin', 'binaryen-shell'), '-remove-imports', '-print-after', os.path.join('test', wasm)], stdout=open('ztemp.wast', 'w'), stderr=subprocess.PIPE) + proc = subprocess.Popen([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 = expected.split('\n') + lines = open('ztemp.wast').read().split('\n') print print '='*80 print lines[start_line-1] print (' '*(start_col-1)) + '^' - print (' '*(start_col-1)) + '|' + print (' '*(start_col-2)) + '/_\\' print '='*80 print err except Exception, e: raise Exception('wasm interpreter error: ' + err) # failed to pretty-print raise Exception('wasm interpreter error') +print '\n[ checking wasm2asm testcases... ]\n' + +for wasm in ['min.wast', 'hello_world.wast', 'unit.wast', 'emcc_O2_hello_world.wast', 'emcc_hello_world.wast']: + if wasm.endswith('.wast'): + print '..', wasm + asm = wasm.replace('.wast', '.2asm.js') + actual, err = subprocess.Popen([os.path.join('bin', 'wasm2asm'), os.path.join('test', wasm)], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + assert err == '', 'bad err:' + err + + # verify output + expected_file = os.path.join('test', asm) + if not os.path.exists(expected_file): + print actual + raise Exception('output ' + expected_file + ' does not exist') + expected = open(expected_file).read() + if actual != expected: + fail(actual, expected) + + open('a.2asm.js', 'w').write(actual) + + if has_node: + # verify asm.js is valid js + proc = subprocess.Popen(['nodejs', 'a.2asm.js'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + assert proc.returncode == 0 + assert not out and not err, [out, err] + + if has_mozjs: + # verify asm.js validates + open('a.2asm.js', 'w').write(actual) + proc = subprocess.Popen(['mozjs', '-w', 'a.2asm.js'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + assert proc.returncode == 0 + fail_if_not_contained(err, 'Successfully compiled asm.js code') + print '\n[ checking binaryen-shell... ]\n' actual, err = subprocess.Popen([os.path.join('bin', 'binaryen-shell'), '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() @@ -80,9 +152,16 @@ fail_if_not_contained(actual, 'options:') fail_if_not_contained(actual, 'passes:') fail_if_not_contained(actual, ' -lower-if-else') -print ' ', ' '.join([os.path.join('bin', 'binaryen-shell'), '-print-before', '-print-after', '-lower-if-else', os.path.join('test', 'if_else.wast')]) -actual, err = subprocess.Popen([os.path.join('bin', 'binaryen-shell'), '-print-before', '-print-after', '-lower-if-else', os.path.join('test', 'if_else.wast')], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() -fail_if_not_identical(actual, open(os.path.join('test', 'if_else.txt')).read()) +print '\n[ checking binaryen-shell passes... ]\n' + +for t in sorted(os.listdir(os.path.join('test', 'passes'))): + if t.endswith('.wast'): + print '..', t + passname = os.path.basename(t).replace('.wast', '') + cmd = [os.path.join('bin', 'binaryen-shell'), '-print-before', '-print-after', '-' + passname, os.path.join('test', 'passes', t)] + print ' ', ' '.join(cmd) + actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + fail_if_not_identical(actual, open(os.path.join('test', 'passes', passname + '.txt')).read()) print '\n[ checking binaryen-shell testcases... ]\n' @@ -91,7 +170,7 @@ for t in tests: print '..', t t = os.path.join('test', t) actual, err = subprocess.Popen([os.path.join('bin', 'binaryen-shell'), t, '-print-before'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() - assert err == '', 'bad err:' + err + assert err.replace('printing before:', '').strip() == '', 'bad err:' + err actual = actual.replace('printing before:\n', '') expected = open(t).read() @@ -133,6 +212,28 @@ for t in spec_tests: if actual != expected: fail(actual, expected) +print '\n[ checking .s testcases... ]\n' + +for s in sorted(os.listdir(os.path.join('test', 'dot_s'))) + sorted(os.listdir(os.path.join('test', 'experimental', 'prototype-wasmate', 'test'))): + if not s.endswith('.s'): continue + if s in ['inline-asm.s', 'userstack.s']: continue + print '..', s + wasm = s.replace('.s', '.wast') + full = os.path.join('test', 'dot_s', s) + if not os.path.exists(full): + full = os.path.join('test', 'experimental', 'prototype-wasmate', 'test', s) + actual, err = subprocess.Popen([os.path.join('bin', 's2wasm'), full], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + assert err == '', 'bad err:' + err + + # verify output + expected_file = os.path.join('test', 'dot_s', wasm) + if not os.path.exists(expected_file): + print actual + raise Exception('output ' + expected_file + ' does not exist') + expected = open(expected_file).read() + if actual != expected: + fail(actual, expected) + print '\n[ checking example testcases... ]\n' subprocess.check_call(['g++', '-std=c++11', os.path.join('test', 'example', 'find_div0s.cpp'), '-Isrc', '-g']) @@ -141,43 +242,105 @@ expected = open(os.path.join('test', 'example', 'find_div0s.txt')).read() if actual != expected: fail(actual, expected) -print '\n[ checking wasm.js polyfill testcases... (need both emcc and nodejs in your path) ]\n' +if has_emcc: -for c in tests: - if c.endswith(('.c', '.cpp')): - print '..', c - base = c.replace('.cpp', '').replace('.c', '') - post = base + '.post.js' - try: - post = open(os.path.join('test', post)).read() - except: - post = None - expected = open(os.path.join('test', base + '.txt')).read() - emcc = os.path.join('test', base + '.emcc') - extra = [] - if os.path.exists(emcc): - extra = json.loads(open(emcc).read()) - if os.path.exists('a.normal.js'): os.unlink('a.normal.js') - for opts in [[], ['-O1'], ['-O2'], ['-O3'], ['-Oz']]: - command = ['emcc', '-o', 'a.wasm.js', '-s', 'BINARYEN="' + os.getcwd() + '"', os.path.join('test', c)] + opts + extra + print '\n[ checking wasm.js methods... ]\n' + + for method in [None, 'asm2wasm', 'wasm-s-parser', 'just-asm']: + for success in [1, 0]: + command = ['emcc', '-o', 'a.wasm.js', '-s', 'BINARYEN="' + os.getcwd() + '"', os.path.join('test', 'hello_world.c') ] + if method: + command += ['-s', 'BINARYEN_METHOD="' + method + '"'] + else: + method = 'wasm-s-parser' # this is the default + print method, ' : ', command, ' => ', success subprocess.check_call(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - print '....' + ' '.join(command) - if post: - open('a.wasm.js', 'a').write(post) + def break_cashew(): + asm = open('a.wasm.asm.js').read() + asm = asm.replace('"almost asm"', '"use asm"; var not_in_asm = [].length + (true || { x: 5 }.x);') + asm = asm.replace("'almost asm'", '"use asm"; var not_in_asm = [].length + (true || { x: 5 }.x);') + open('a.wasm.asm.js', 'w').write(asm) + if method == 'asm2wasm': + os.unlink('a.wasm.wast') # we should not need the .wast + if not success: + break_cashew() # we need cashew + elif method == 'wasm-s-parser': + os.unlink('a.wasm.asm.js') # we should not need the .asm.js + if not success: + os.unlink('a.wasm.wast.mappedGlobals') + elif method == 'just-asm': + os.unlink('a.wasm.wast') # we should not need the .wast + break_cashew() # we don't use cashew, so ok to break it + if not success: + os.unlink('a.wasm.js') else: - print ' (no post)' - for which in ['wasm']: - print '......', which - try: - args = json.loads(open(os.path.join('test', base + '.args')).read()) - except: - args = [] - print ' (no args)' - proc = subprocess.Popen(['nodejs', 'a.' + which + '.js'] + args, stdout=subprocess.PIPE) + 1/0 + if has_node: + proc = subprocess.Popen(['nodejs', 'a.wasm.js'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate() - assert proc.returncode == 0 - if out.strip() != expected.strip(): - fail(out, expected) + if success: + assert proc.returncode == 0 + assert 'hello, world!' in out + else: + assert proc.returncode != 0 + assert 'hello, world!' not in out + + print '\n[ checking emcc WASM_BACKEND testcases... ]\n' + + for c in ['hello_world.cpp', 'hello_num.cpp']: + print '..', c + base = c.replace('.cpp', '').replace('.c', '') + expected = open(os.path.join('test', 'wasm_backend', base + '.txt')).read() + command = ['emcc', '-o', 'a.wasm.js', '-s', 'BINARYEN="' + os.getcwd() + '"', os.path.join('test', 'wasm_backend', c), '-O1', '-s', 'WASM_BACKEND=1', '-s', 'ONLY_MY_CODE=1'] + print '....' + ' '.join(command) + subprocess.check_call(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if has_node: + proc = subprocess.Popen(['nodejs', 'a.wasm.js'], stdout=subprocess.PIPE) + out, err = proc.communicate() + assert proc.returncode == 0 + if out.strip() != expected.strip(): + fail(out, expected) + + print '\n[ checking wasm.js testcases... ]\n' + + for c in tests: + if c.endswith(('.c', '.cpp')): + print '..', c + base = c.replace('.cpp', '').replace('.c', '') + post = base + '.post.js' + try: + post = open(os.path.join('test', post)).read() + except: + post = None + expected = open(os.path.join('test', base + '.txt')).read() + emcc = os.path.join('test', base + '.emcc') + extra = [] + if os.path.exists(emcc): + extra = json.loads(open(emcc).read()) + if os.path.exists('a.normal.js'): os.unlink('a.normal.js') + for opts in [[], ['-O1'], ['-O2'], ['-O3'], ['-Oz']]: + for method in ['asm2wasm', 'wasm-s-parser', 'just-asm']: + command = ['emcc', '-o', 'a.wasm.js', '-s', 'BINARYEN="' + os.getcwd() + '"', os.path.join('test', c)] + opts + extra + command += ['-s', 'BINARYEN_METHOD="' + method + '"'] + subprocess.check_call(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + print '....' + ' '.join(command) + if post: + open('a.wasm.js', 'a').write(post) + else: + print ' (no post)' + for which in ['wasm']: + print '......', which + try: + args = json.loads(open(os.path.join('test', base + '.args')).read()) + except: + args = [] + print ' (no args)' + if has_node: + proc = subprocess.Popen(['nodejs', 'a.' + which + '.js'] + args, stdout=subprocess.PIPE) + out, err = proc.communicate() + assert proc.returncode == 0 + if out.strip() != expected.strip(): + fail(out, expected) print '\n[ success! ]' |