blob: 761fa456be4e4244c7b231cba1f82acdc1842dbf [file] [log] [blame]
#!/usr/bin/env python3
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
import glob
import optparse as op
import os
import subprocess as sp
import sys
import time
import re
import xml.dom.minidom as md
USAGE = "%prog [options] [command to run...]"
TEST_PATH = os.path.dirname(os.path.abspath(__file__))
ROOT_PATH = os.path.dirname(os.path.dirname(TEST_PATH))
N = 3
COUCHJS = "src/couch/priv/couchjs"
SCRIPTS = """
test/javascript/json2.js
test/javascript/sha1.js
test/javascript/couch.js
test/javascript/replicator_db_inc.js
test/javascript/couch_test_runner.js
test/javascript/couch_http.js
test/javascript/test_setup.js
share/server/util.js
""".split()
RUNNER = "test/javascript/cli_runner.js"
def mkformatter(tests):
longest = max([len(x) for x in tests])
green = "\033[32m"
orange = "\033[33m"
red = "\033[31m"
clear = "\033[0m"
if not sys.stderr.isatty():
green, orange, red, clear = "", "", "", ""
def _colorized(rval):
if rval == 0:
return green + "pass" + clear
elif rval == 2:
return orange + "skipped" + clear
elif rval == 3:
return green + "ported to elixir" + clear
else:
return red + ("fail: %d" % rval) + clear
def _fmt(test):
if isinstance(test, str):
padding = (longest - len(test)) * " "
sys.stderr.write(test + " " + padding)
sys.stderr.flush()
elif isinstance(test, int):
if test:
sys.stderr.write(_colorized(test) + os.linesep)
else:
sys.stderr.write(_colorized(test) + os.linesep)
sys.stderr.flush()
return _fmt
def run_couchjs(test, fmt):
fmt(test)
cmd = (
[COUCHJS, "--eval", "-H", "-T"]
+ ["-u", "test/javascript/couchdb.uri"]
+ SCRIPTS
+ [test, RUNNER]
)
p = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.STDOUT)
output = []
while True:
line = p.stdout.readline()
if not line:
break
line = line.decode()
output.append(line)
sys.stderr.write(line)
p.wait()
fmt(p.returncode)
return (p.returncode, "".join(output))
def write_junit(filename, total_time, results):
failures = 0
skipped = 0
for (_, rc, _, _) in results:
if rc == 2 or rc == 3:
skipped += 1
else:
failures += 1
doc = md.Document()
root = doc.createElement("testsuite")
root.setAttribute("name", "JavaScript tests")
root.setAttribute("time", "%0.3f" % total_time)
root.setAttribute("tests", str(len(results)))
root.setAttribute("failures", str(failures))
root.setAttribute("errors", "0")
root.setAttribute("skipped", str(skipped))
doc.appendChild(root)
for (path, rc, output, test_time) in results:
sys.stderr.write("WHUT? %r\n" % output)
name = os.path.split(path)[-1]
tc = doc.createElement("testcase")
tc.setAttribute("name", name)
tc.setAttribute("time", "%0.3f" % test_time)
if rc == 0:
pass
elif rc == 2:
skipped = doc.createElement("skipped")
skipped.setAttribute("message", "disabled")
tc.appendChild(skipped)
elif rc == 3:
skipped = doc.createElement("skipped")
skipped.setAttribute("message", "ported to elixir")
tc.appendChild(skipped)
else:
failure = doc.createElement("failure")
failure.setAttribute("message", "failed: %d" % rc)
failure_text = "Exit Code: %d" % rc + "\n\n" + output
message = doc.createTextNode(failure_text)
failure.appendChild(message)
tc.appendChild(failure)
root.appendChild(tc)
with open(filename, "w") as handle:
doc.writexml(handle, addindent=" ", newl=os.linesep)
def options():
return [
op.make_option(
"-s",
"--start",
metavar="FILENAME",
default=None,
help="Start from the given filename if multiple files are passed",
),
op.make_option(
"-a",
"--all",
action="store_true",
dest="all",
help="Run all tests, even if one or more fail",
),
op.make_option(
"-i",
"--ignore",
type="string",
action="callback",
default=None,
callback=get_delimited_list,
dest="ignore",
help="Ignore test suites",
),
op.make_option(
"-u",
"--suites",
type="string",
action="callback",
default=None,
callback=get_delimited_list,
dest="suites",
help="Run specific suites",
),
op.make_option(
"-p",
"--path",
type="string",
default="test/javascript/tests",
dest="test_path",
help="Path where the tests are located",
),
op.make_option(
"-j",
"--junit-report",
type="string",
default="test/javascript/junit.xml",
dest="junit_report",
help="Write a JUnit compatible test report",
),
]
def main():
parser = op.OptionParser(usage=USAGE, option_list=options())
opts, args = parser.parse_args()
run_list = []
ignore_list = []
tests = []
run_list = [opts.test_path] if not opts.suites else opts.suites
run_list = build_test_case_paths(opts.test_path, run_list)
ignore_list = build_test_case_paths(opts.test_path, opts.ignore)
# sort is needed because certain tests fail if executed out of order
tests = sorted(list(set(run_list) - set(ignore_list)))
if opts.start is not None:
tmp = []
for name in tests:
if name >= opts.start:
tmp.append(name)
tests = tmp
results = []
begin = time.time()
passed = 0
failed = 0
if len(tests) > 0:
fmt = mkformatter(tests)
for test in tests:
tbefore = time.time()
(result, output) = run_couchjs(test, fmt)
results.append((test, result, output, time.time() - tbefore))
if result == 0 or result == 2 or result == 3:
passed += 1
else:
failed += 1
if not opts.all:
break
total_time = time.time() - begin
if opts.junit_report:
write_junit(opts.junit_report, total_time, results)
sys.stderr.write(
"=======================================================" + os.linesep
)
sys.stderr.write("JavaScript tests complete." + os.linesep)
sys.stderr.write(
" Failed: {0}. Skipped or passed: {1}.".format(failed, passed) + os.linesep
)
exit(failed > 0)
def build_test_case_paths(path, args=None):
tests = []
if args is None:
args = []
for name in args:
if os.path.isdir(name):
tests.extend(sorted(glob.glob(os.path.join(name, "*.js"))))
elif os.path.isfile(name):
check = tests.append(name)
else:
pname = os.path.join(path, name)
if os.path.isfile(pname):
tests.append(pname)
elif os.path.isfile(pname + ".js"):
tests.append(pname + ".js")
else:
sys.stderr.write("Waring - Unknown test: " + name + os.linesep)
return tests
def get_delimited_list(option, opt, value, parser):
delimited = [i for i in re.split(r",|\s", value.strip()) if i]
setattr(parser.values, option.dest, delimited)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass