Skip to content

Commit fe0b16a

Browse files
authored
Discover and run unit tests from check.py (WebAssembly#1948)
unittest is Python's standard testing framework, so this change allows arbitrary tests to be written without introducing any new dependencies or code in check.py. A new test that was not possible to write before is also included. It is the first of many.
1 parent 1787295 commit fe0b16a

4 files changed

Lines changed: 97 additions & 6 deletions

File tree

check.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,22 @@
1818
import shutil
1919
import subprocess
2020
import sys
21+
import unittest
2122

2223
from scripts.test.support import run_command, split_wast, node_test_glue, node_has_webassembly
2324
from scripts.test.shared import (
2425
BIN_DIR, MOZJS, NATIVECC, NATIVEXX, NODEJS, BINARYEN_JS,
2526
WASM_AS, WASM_CTOR_EVAL, WASM_OPT, WASM_SHELL, WASM_MERGE, WASM_METADCE,
2627
WASM_DIS, WASM_REDUCE, binary_format_check, delete_from_orbit, fail, fail_with_error,
2728
fail_if_not_identical, fail_if_not_contained, has_vanilla_emcc,
28-
has_vanilla_llvm, minify_check, num_failures, options, tests,
29-
requested, warnings, has_shell_timeout, fail_if_not_identical_to_file
29+
has_vanilla_llvm, minify_check, options, tests, requested, warnings,
30+
has_shell_timeout, fail_if_not_identical_to_file
3031
)
3132

33+
# For shared.num_failures. Cannot import directly because modifications made in
34+
# shared.py would not affect the version imported here.
35+
import scripts.test.shared as shared
36+
3237
import scripts.test.asm2wasm as asm2wasm
3338
import scripts.test.lld as lld
3439
import scripts.test.wasm2js as wasm2js
@@ -592,6 +597,17 @@ def run_gcc_tests():
592597
fail_if_not_identical_to_file(actual, expected)
593598

594599

600+
def run_unittest():
601+
print '\n[ checking unit tests...]\n'
602+
603+
# equivalent to `python -m unittest discover -s ./test -v`
604+
suite = unittest.defaultTestLoader.discover(os.path.dirname(options.binaryen_test))
605+
result = unittest.TextTestRunner(verbosity=2, failfast=options.abort_on_first_failure).run(suite)
606+
shared.num_failures += len(result.errors) + len(result.failures)
607+
if options.abort_on_first_failure and shared.num_failures:
608+
raise Exception("unittest failed")
609+
610+
595611
# Run all the tests
596612
def main():
597613
run_help_tests()
@@ -618,17 +634,20 @@ def main():
618634
if options.run_gcc_tests:
619635
run_gcc_tests()
620636

637+
run_unittest()
638+
621639
# Check/display the results
622-
if num_failures == 0:
640+
if shared.num_failures == 0:
623641
print '\n[ success! ]'
624642

625643
if warnings:
626644
print '\n' + '\n'.join(warnings)
627645

628-
if num_failures > 0:
629-
print '\n[ ' + str(num_failures) + ' failures! ]'
646+
if shared.num_failures > 0:
647+
print '\n[ ' + str(shared.num_failures) + ' failures! ]'
648+
return 1
630649

631-
return num_failures
650+
return 0
632651

633652

634653
if __name__ == '__main__':

scripts/test/shared.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,60 @@ def remove_readonly_and_try_again(func, path, exc_info):
281281
pass
282282

283283

284+
# This is a workaround for https://bugs.python.org/issue9400
285+
class Py2CalledProcessError(subprocess.CalledProcessError):
286+
def __init__(self, returncode, cmd, output=None, stderr=None):
287+
super(Exception, self).__init__(returncode, cmd, output, stderr)
288+
self.returncode = returncode
289+
self.cmd = cmd
290+
self.output = output
291+
self.stderr = stderr
292+
293+
294+
# https://docs.python.org/3/library/subprocess.html#subprocess.CompletedProcess
295+
class Py2CompletedProcess:
296+
def __init__(self, args, returncode, stdout, stderr):
297+
self.args = args
298+
self.returncode = returncode
299+
self.stdout = stdout
300+
self.stderr = stderr
301+
302+
def __repr__(self):
303+
_repr = ['args=%s, returncode=%s' % (self.args, self.returncode)]
304+
if self.stdout is not None:
305+
_repr += 'stdout=' + repr(self.stdout)
306+
if self.stderr is not None:
307+
_repr += 'stderr=' + repr(self.stderr)
308+
return 'CompletedProcess(%s)' % ', '.join(_repr)
309+
310+
def check_returncode(self):
311+
if self.returncode != 0:
312+
raise Py2CalledProcessError(returncode=self.returncode, cmd=self.args,
313+
output=self.stdout, stderr=self.stderr)
314+
315+
316+
def run_process(cmd, check=True, input=None, universal_newlines=True,
317+
capture_output=False, *args, **kw):
318+
kw.setdefault('universal_newlines', True)
319+
320+
if hasattr(subprocess, "run"):
321+
ret = subprocess.run(cmd, check=check, input=input, *args, **kw)
322+
return ret
323+
324+
# Python 2 compatibility: Introduce Python 3 subprocess.run-like behavior
325+
if input is not None:
326+
kw['stdin'] = subprocess.PIPE
327+
if capture_output:
328+
kw['stdout'] = subprocess.PIPE
329+
kw['stderr'] = subprocess.PIPE
330+
proc = subprocess.Popen(cmd, *args, **kw)
331+
stdout, stderr = proc.communicate(input)
332+
result = Py2CompletedProcess(cmd, proc.returncode, stdout, stderr)
333+
if check:
334+
result.check_returncode()
335+
return result
336+
337+
284338
def fail_with_error(msg):
285339
global num_failures
286340
try:

test/crash/__init__.py

Whitespace-only changes.

test/crash/test_features.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import unittest
2+
from scripts.test.shared import WASM_OPT, run_process
3+
4+
5+
class FeatureValidationTest(unittest.TestCase):
6+
def test_simd_type(self):
7+
module = """
8+
(module
9+
(func $foo (param $0 v128) (result v128)
10+
(local.get $0)
11+
)
12+
)
13+
"""
14+
p = run_process(WASM_OPT + ['--mvp-features', '--print'],
15+
input=module, check=False, capture_output=True)
16+
self.assertIn("all used types should be allowed", p.stderr)
17+
self.assertIn("Fatal: error in validating input", p.stderr)
18+
self.assertNotEqual(p.returncode, 0)

0 commit comments

Comments
 (0)