forked from Mirror/wren
Mainly to get rid of one top level directory. But this will also be useful when there are tests of the embedding API.
262 lines
7.6 KiB
Python
Executable File
262 lines
7.6 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
from __future__ import print_function
|
|
|
|
from collections import defaultdict
|
|
from os import listdir
|
|
from os.path import abspath, dirname, isdir, isfile, join, realpath, relpath, splitext
|
|
import re
|
|
from subprocess import Popen, PIPE
|
|
import sys
|
|
|
|
# Runs the tests.
|
|
WREN_DIR = dirname(dirname(realpath(__file__)))
|
|
WREN_APP = join(WREN_DIR, 'bin', 'wrend')
|
|
|
|
EXPECT_PATTERN = re.compile(r'// expect: (.*)')
|
|
EXPECT_ERROR_PATTERN = re.compile(r'// expect error')
|
|
EXPECT_ERROR_LINE_PATTERN = re.compile(r'// expect error line (\d+)')
|
|
EXPECT_RUNTIME_ERROR_PATTERN = re.compile(r'// expect runtime error: (.+)')
|
|
ERROR_PATTERN = re.compile(r'\[.* line (\d+)\] Error')
|
|
STACK_TRACE_PATTERN = re.compile(r'\[.* line (\d+)\] in')
|
|
STDIN_PATTERN = re.compile(r'// stdin: (.*)')
|
|
SKIP_PATTERN = re.compile(r'// skip: (.*)')
|
|
NONTEST_PATTERN = re.compile(r'// nontest')
|
|
|
|
if sys.platform == 'win32':
|
|
class color:
|
|
GREEN = ''
|
|
RED = ''
|
|
DEFAULT = ''
|
|
PINK = ''
|
|
YELLOW = ''
|
|
else:
|
|
class color:
|
|
GREEN = '\033[32m'
|
|
RED = '\033[31m'
|
|
DEFAULT = '\033[0m'
|
|
PINK = '\033[91m'
|
|
YELLOW = '\033[33m'
|
|
|
|
passed = 0
|
|
failed = 0
|
|
skipped = defaultdict(int)
|
|
num_skipped = 0
|
|
|
|
|
|
def walk(dir, callback):
|
|
""" Walks [dir], and executes [callback] on each file. """
|
|
dir = abspath(dir)
|
|
for file in [file for file in listdir(dir) if not file in [".",".."]]:
|
|
nfile = join(dir, file)
|
|
if isdir(nfile):
|
|
walk(nfile, callback)
|
|
else:
|
|
callback(nfile)
|
|
|
|
|
|
def print_line(line=None):
|
|
# Erase the line.
|
|
print('\033[2K', end='')
|
|
# Move the cursor to the beginning.
|
|
print('\r', end='')
|
|
if line:
|
|
print(line, end='')
|
|
sys.stdout.flush()
|
|
|
|
|
|
def run_test(path):
|
|
global passed
|
|
global failed
|
|
global skipped
|
|
global num_skipped
|
|
|
|
if (splitext(path)[1] != '.wren'):
|
|
return
|
|
|
|
# Check if we are just running a subset of the tests.
|
|
if len(sys.argv) == 2:
|
|
this_test = relpath(path, join(WREN_DIR, 'test'))
|
|
if not this_test.startswith(sys.argv[1]):
|
|
return
|
|
|
|
# Make a nice short path relative to the working directory.
|
|
|
|
# Normalize it to use "/" since, among other things, wren expects its argument
|
|
# to use that.
|
|
path = relpath(path).replace("\\", "/")
|
|
|
|
# Read the test and parse out the expectations.
|
|
expect_output = []
|
|
expect_error = []
|
|
expect_runtime_error_line = 0
|
|
expect_runtime_error = None
|
|
expect_return = 0
|
|
|
|
input_lines = []
|
|
|
|
print_line('Passed: ' + color.GREEN + str(passed) + color.DEFAULT +
|
|
' Failed: ' + color.RED + str(failed) + color.DEFAULT +
|
|
' Skipped: ' + color.YELLOW + str(num_skipped) + color.DEFAULT)
|
|
|
|
line_num = 1
|
|
with open(path, 'r') as file:
|
|
for line in file:
|
|
match = EXPECT_PATTERN.search(line)
|
|
if match:
|
|
expect_output.append((match.group(1), line_num))
|
|
|
|
match = EXPECT_ERROR_PATTERN.search(line)
|
|
if match:
|
|
expect_error.append(line_num)
|
|
# If we expect compile errors, it should exit with EX_DATAERR.
|
|
expect_return = 65
|
|
|
|
match = EXPECT_ERROR_LINE_PATTERN.search(line)
|
|
if match:
|
|
expect_error.append(int(match.group(1)))
|
|
# If we expect compile errors, it should exit with EX_DATAERR.
|
|
expect_return = 65
|
|
|
|
match = EXPECT_RUNTIME_ERROR_PATTERN.search(line)
|
|
if match:
|
|
expect_runtime_error_line = line_num
|
|
expect_runtime_error = match.group(1)
|
|
# If we expect a runtime error, it should exit with EX_SOFTWARE.
|
|
expect_return = 70
|
|
|
|
match = STDIN_PATTERN.search(line)
|
|
if match:
|
|
input_lines.append(match.group(1) + '\n')
|
|
|
|
match = SKIP_PATTERN.search(line)
|
|
if match:
|
|
num_skipped += 1
|
|
skipped[match.group(1)] += 1
|
|
return
|
|
|
|
match = NONTEST_PATTERN.search(line)
|
|
if match:
|
|
# Not a test file at all, so ignore it.
|
|
return
|
|
|
|
line_num += 1
|
|
|
|
# If any input is fed to the test in stdin, concatetate it into one string.
|
|
input_bytes = None
|
|
if len(input_lines) > 0:
|
|
input_bytes = "".join(input_lines).encode("utf-8")
|
|
|
|
# Invoke wren and run the test.
|
|
proc = Popen([WREN_APP, path], stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
|
(out, err) = proc.communicate(input_bytes)
|
|
|
|
fails = []
|
|
|
|
try:
|
|
out = out.decode("utf-8").replace('\r\n', '\n')
|
|
err = err.decode("utf-8").replace('\r\n', '\n')
|
|
except:
|
|
fails.append('Error decoding output.')
|
|
|
|
# Validate that no unexpected errors occurred.
|
|
if expect_return != 0 and err != '':
|
|
lines = err.split('\n')
|
|
if expect_runtime_error:
|
|
# Make sure we got the right error.
|
|
if lines[0] != expect_runtime_error:
|
|
fails.append('Expected runtime error "' + expect_runtime_error +
|
|
'" and got:')
|
|
fails.append(lines[0])
|
|
|
|
# Make sure the stack trace has the right line.
|
|
match = STACK_TRACE_PATTERN.search(lines[1])
|
|
if not match:
|
|
fails.append('Expected stack trace and got:')
|
|
fails.append(lines[1])
|
|
else:
|
|
stack_line = int(match.group(1))
|
|
if stack_line != expect_runtime_error_line:
|
|
fails.append('Expected runtime error on line ' +
|
|
str(expect_runtime_error_line) + ' but was on line ' +
|
|
str(stack_line))
|
|
else:
|
|
lines = err.split('\n')
|
|
while len(lines) > 0:
|
|
line = lines.pop(0)
|
|
match = ERROR_PATTERN.search(line)
|
|
if match:
|
|
if float(match.group(1)) not in expect_error:
|
|
fails.append('Unexpected error:')
|
|
fails.append(line)
|
|
elif line != '':
|
|
fails.append('Unexpected output on stderr:')
|
|
fails.append(line)
|
|
else:
|
|
for line in expect_error:
|
|
fails.append('Expected error on line ' + str(line) + ' and got none.')
|
|
if expect_runtime_error:
|
|
fails.append('Expected runtime error "' + expect_runtime_error +
|
|
'" and got none.')
|
|
|
|
# Validate the exit code.
|
|
if proc.returncode != expect_return:
|
|
fails.append('Expected return code {0} and got {1}. Stderr:'
|
|
.format(expect_return, proc.returncode))
|
|
fails += err.split('\n')
|
|
else:
|
|
# Validate the output.
|
|
expect_index = 0
|
|
|
|
# Remove the trailing last empty line.
|
|
out_lines = out.split('\n')
|
|
if out_lines[-1] == '':
|
|
del out_lines[-1]
|
|
|
|
for line in out_lines:
|
|
if sys.version_info < (3, 0):
|
|
line = line.encode('utf-8')
|
|
if expect_index >= len(expect_output):
|
|
fails.append('Got output "{0}" when none was expected.'.format(line))
|
|
elif expect_output[expect_index][0] != line:
|
|
fails.append('Expected output "{0}" on line {1} and got "{2}".'.
|
|
format(expect_output[expect_index][0],
|
|
expect_output[expect_index][1], line))
|
|
expect_index += 1
|
|
|
|
while expect_index < len(expect_output):
|
|
fails.append('Missing expected output "{0}" on line {1}.'.
|
|
format(expect_output[expect_index][0],
|
|
expect_output[expect_index][1]))
|
|
expect_index += 1
|
|
|
|
# Display the results.
|
|
if len(fails) == 0:
|
|
passed += 1
|
|
#print color.GREEN + 'PASS' + color.DEFAULT + ': ' + path
|
|
else:
|
|
failed += 1
|
|
print_line(color.RED + 'FAIL' + color.DEFAULT + ': ' + path)
|
|
print('')
|
|
for fail in fails:
|
|
print(' ' + color.PINK + fail + color.DEFAULT)
|
|
print('')
|
|
|
|
|
|
for dir in ['core', 'io', 'language', 'limit']:
|
|
walk(join(WREN_DIR, 'test', dir), run_test)
|
|
|
|
print_line()
|
|
if failed == 0:
|
|
print('All ' + color.GREEN + str(passed) + color.DEFAULT + ' tests passed.')
|
|
else:
|
|
print(color.GREEN + str(passed) + color.DEFAULT + ' tests passed. ' +
|
|
color.RED + str(failed) + color.DEFAULT + ' tests failed.')
|
|
|
|
for key in sorted(skipped.keys()):
|
|
print('Skipped ' + color.YELLOW + str(skipped[key]) + color.DEFAULT +
|
|
' tests: ' + key)
|
|
|
|
if failed != 0:
|
|
sys.exit(1)
|