2015-01-05 20:09:23 -06:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
|
|
from __future__ import print_function
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
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.
|
2014-01-23 23:29:50 -08:00
|
|
|
WREN_DIR = dirname(dirname(realpath(__file__)))
|
2015-02-22 10:42:21 -08:00
|
|
|
WREN_APP = join(WREN_DIR, 'bin', 'wrend')
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
EXPECT_PATTERN = re.compile(r'// expect: (.*)')
|
2013-10-28 07:12:39 -07:00
|
|
|
EXPECT_ERROR_PATTERN = re.compile(r'// expect error')
|
2013-11-09 21:32:57 -08:00
|
|
|
EXPECT_ERROR_LINE_PATTERN = re.compile(r'// expect error line (\d+)')
|
2014-01-01 13:25:24 -08:00
|
|
|
EXPECT_RUNTIME_ERROR_PATTERN = re.compile(r'// expect runtime error: (.+)')
|
|
|
|
|
ERROR_PATTERN = re.compile(r'\[.* line (\d+)\] Error')
|
2014-04-14 07:23:05 -07:00
|
|
|
STACK_TRACE_PATTERN = re.compile(r'\[.* line (\d+)\] in')
|
2015-01-11 21:47:29 -08:00
|
|
|
STDIN_PATTERN = re.compile(r'// stdin: (.*)')
|
2013-10-27 22:45:40 -07:00
|
|
|
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)
|
2015-01-15 15:59:14 -08:00
|
|
|
num_skipped = 0
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
2015-01-05 20:09:23 -06:00
|
|
|
print('\033[2K', end='')
|
2013-10-27 22:45:40 -07:00
|
|
|
# Move the cursor to the beginning.
|
2015-01-05 20:09:23 -06:00
|
|
|
print('\r', end='')
|
2013-10-27 22:45:40 -07:00
|
|
|
if line:
|
2015-01-05 20:09:23 -06:00
|
|
|
print(line, end='')
|
2013-10-27 22:45:40 -07:00
|
|
|
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.
|
2015-03-13 07:26:23 -07:00
|
|
|
|
|
|
|
|
# Normalize it to use "/" since, among other things, wren expects its argument
|
|
|
|
|
# to use that.
|
|
|
|
|
path = relpath(path).replace("\\", "/")
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
# Read the test and parse out the expectations.
|
|
|
|
|
expect_output = []
|
|
|
|
|
expect_error = []
|
2014-01-01 13:25:24 -08:00
|
|
|
expect_runtime_error_line = 0
|
|
|
|
|
expect_runtime_error = None
|
2013-10-27 22:45:40 -07:00
|
|
|
expect_return = 0
|
|
|
|
|
|
2015-01-11 21:47:29 -08:00
|
|
|
input_lines = []
|
|
|
|
|
|
2013-10-27 22:45:40 -07:00
|
|
|
print_line('Passed: ' + color.GREEN + str(passed) + color.DEFAULT +
|
2013-11-09 21:32:57 -08:00
|
|
|
' Failed: ' + color.RED + str(failed) + color.DEFAULT +
|
|
|
|
|
' Skipped: ' + color.YELLOW + str(num_skipped) + color.DEFAULT)
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
line_num = 1
|
|
|
|
|
with open(path, 'r') as file:
|
|
|
|
|
for line in file:
|
|
|
|
|
match = EXPECT_PATTERN.search(line)
|
|
|
|
|
if match:
|
2013-11-09 21:32:57 -08:00
|
|
|
expect_output.append((match.group(1), line_num))
|
2013-10-27 22:45:40 -07:00
|
|
|
|
2013-10-28 07:12:39 -07:00
|
|
|
match = EXPECT_ERROR_PATTERN.search(line)
|
|
|
|
|
if match:
|
2013-11-09 21:32:57 -08:00
|
|
|
expect_error.append(line_num)
|
2014-01-01 13:25:24 -08:00
|
|
|
# If we expect compile errors, it should exit with EX_DATAERR.
|
|
|
|
|
expect_return = 65
|
2013-11-09 21:32:57 -08:00
|
|
|
|
|
|
|
|
match = EXPECT_ERROR_LINE_PATTERN.search(line)
|
|
|
|
|
if match:
|
|
|
|
|
expect_error.append(int(match.group(1)))
|
2014-01-01 13:25:24 -08:00
|
|
|
# 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
|
2013-10-28 07:12:39 -07:00
|
|
|
|
2015-01-11 21:47:29 -08:00
|
|
|
match = STDIN_PATTERN.search(line)
|
|
|
|
|
if match:
|
|
|
|
|
input_lines.append(match.group(1) + '\n')
|
|
|
|
|
|
2013-10-27 22:45:40 -07:00
|
|
|
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
|
|
|
|
|
|
2015-01-11 21:47:29 -08:00
|
|
|
# If any input is fed to the test in stdin, concatetate it into one string.
|
2015-02-27 07:22:27 -08:00
|
|
|
input_bytes = None
|
|
|
|
|
if len(input_lines) > 0:
|
|
|
|
|
input_bytes = "".join(input_lines).encode("utf-8")
|
2015-01-11 21:47:29 -08:00
|
|
|
|
2013-10-27 22:45:40 -07:00
|
|
|
# Invoke wren and run the test.
|
2015-01-11 21:47:29 -08:00
|
|
|
proc = Popen([WREN_APP, path], stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
2015-02-27 07:22:27 -08:00
|
|
|
(out, err) = proc.communicate(input_bytes)
|
2015-03-14 08:47:31 -07:00
|
|
|
|
2015-03-14 09:49:55 -07:00
|
|
|
fails = []
|
|
|
|
|
|
2015-03-14 08:47:31 -07:00
|
|
|
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.')
|
2013-10-27 22:45:40 -07:00
|
|
|
|
2013-10-28 07:12:39 -07:00
|
|
|
# Validate that no unexpected errors occurred.
|
2014-01-01 13:25:24 -08:00
|
|
|
if expect_return != 0 and err != '':
|
2013-10-28 07:12:39 -07:00
|
|
|
lines = err.split('\n')
|
2014-01-01 13:25:24 -08:00
|
|
|
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:')
|
2013-10-28 07:12:39 -07:00
|
|
|
fails.append(line)
|
|
|
|
|
else:
|
|
|
|
|
for line in expect_error:
|
|
|
|
|
fails.append('Expected error on line ' + str(line) + ' and got none.')
|
2014-01-01 13:25:24 -08:00
|
|
|
if expect_runtime_error:
|
2014-01-03 10:47:26 -08:00
|
|
|
fails.append('Expected runtime error "' + expect_runtime_error +
|
|
|
|
|
'" and got none.')
|
2013-10-28 07:12:39 -07:00
|
|
|
|
2013-10-27 22:45:40 -07:00
|
|
|
# Validate the exit code.
|
2013-10-28 07:12:39 -07:00
|
|
|
if proc.returncode != expect_return:
|
2014-04-02 19:41:53 -07:00
|
|
|
fails.append('Expected return code {0} and got {1}. Stderr:'
|
2013-10-28 07:12:39 -07:00
|
|
|
.format(expect_return, proc.returncode))
|
2014-04-02 19:41:53 -07:00
|
|
|
fails += err.split('\n')
|
2013-10-27 22:45:40 -07:00
|
|
|
else:
|
|
|
|
|
# Validate the output.
|
|
|
|
|
expect_index = 0
|
|
|
|
|
|
|
|
|
|
# Remove the trailing last empty line.
|
|
|
|
|
out_lines = out.split('\n')
|
|
|
|
|
if out_lines[-1] == '':
|
2013-10-28 07:12:39 -07:00
|
|
|
del out_lines[-1]
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
for line in out_lines:
|
2015-01-05 20:09:23 -06:00
|
|
|
if sys.version_info < (3, 0):
|
|
|
|
|
line = line.encode('utf-8')
|
2013-10-28 07:12:39 -07:00
|
|
|
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
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
while expect_index < len(expect_output):
|
2013-10-28 07:12:39 -07:00
|
|
|
fails.append('Missing expected output "{0}" on line {1}.'.
|
|
|
|
|
format(expect_output[expect_index][0],
|
|
|
|
|
expect_output[expect_index][1]))
|
|
|
|
|
expect_index += 1
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
# Display the results.
|
|
|
|
|
if len(fails) == 0:
|
|
|
|
|
passed += 1
|
2013-10-28 06:53:51 -07:00
|
|
|
#print color.GREEN + 'PASS' + color.DEFAULT + ': ' + path
|
2013-10-27 22:45:40 -07:00
|
|
|
else:
|
|
|
|
|
failed += 1
|
|
|
|
|
print_line(color.RED + 'FAIL' + color.DEFAULT + ': ' + path)
|
2015-02-27 07:22:27 -08:00
|
|
|
print('')
|
2013-10-27 22:45:40 -07:00
|
|
|
for fail in fails:
|
2015-01-05 20:09:23 -06:00
|
|
|
print(' ' + color.PINK + fail + color.DEFAULT)
|
2015-02-27 07:22:27 -08:00
|
|
|
print('')
|
2013-10-27 22:45:40 -07:00
|
|
|
|
2015-03-14 12:45:56 -07:00
|
|
|
|
2015-03-22 14:16:53 -07:00
|
|
|
for dir in ['core', 'io', 'language', 'limit', 'meta']:
|
2015-03-14 12:45:56 -07:00
|
|
|
walk(join(WREN_DIR, 'test', dir), run_test)
|
2013-10-27 22:45:40 -07:00
|
|
|
|
|
|
|
|
print_line()
|
|
|
|
|
if failed == 0:
|
2015-01-05 20:09:23 -06:00
|
|
|
print('All ' + color.GREEN + str(passed) + color.DEFAULT + ' tests passed.')
|
2013-10-27 22:45:40 -07:00
|
|
|
else:
|
2015-01-05 20:09:23 -06:00
|
|
|
print(color.GREEN + str(passed) + color.DEFAULT + ' tests passed. ' +
|
2013-10-27 22:45:40 -07:00
|
|
|
color.RED + str(failed) + color.DEFAULT + ' tests failed.')
|
|
|
|
|
|
|
|
|
|
for key in sorted(skipped.keys()):
|
2015-01-05 20:09:23 -06:00
|
|
|
print('Skipped ' + color.YELLOW + str(skipped[key]) + color.DEFAULT +
|
2013-10-27 22:45:40 -07:00
|
|
|
' tests: ' + key)
|
2015-01-02 08:13:51 -08:00
|
|
|
|
|
|
|
|
if failed != 0:
|
2015-01-05 20:09:23 -06:00
|
|
|
sys.exit(1)
|