1#!/usr/bin/env python3
2# This Source Code Form is subject to the terms of the Mozilla Public
3# License, v. 2.0. If a copy of the MPL was not distributed with this
4# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6from __future__ import print_function
7
8import os
9import site
10import subprocess
11import argparse
12
13from glob import glob
14
15scriptdir = os.path.abspath(os.path.dirname(__file__))
16testdir = os.path.join(scriptdir, "t")
17
18site.addsitedir(testdir)
19from testlib import Test, equal
20
21parser = argparse.ArgumentParser(description="run hazard analysis tests")
22parser.add_argument(
23    "--js", default=os.environ.get("JS"), help="JS binary to run the tests with"
24)
25parser.add_argument(
26    "--sixgill",
27    default=os.environ.get("SIXGILL", os.path.join(testdir, "sixgill")),
28    help="Path to root of sixgill installation",
29)
30parser.add_argument(
31    "--sixgill-bin",
32    default=os.environ.get("SIXGILL_BIN"),
33    help="Path to sixgill binary dir",
34)
35parser.add_argument(
36    "--sixgill-plugin",
37    default=os.environ.get("SIXGILL_PLUGIN"),
38    help="Full path to sixgill gcc plugin",
39)
40parser.add_argument(
41    "--gccdir", default=os.environ.get("GCCDIR"), help="Path to GCC installation dir"
42)
43parser.add_argument("--cc", default=os.environ.get("CC"), help="Path to gcc")
44parser.add_argument("--cxx", default=os.environ.get("CXX"), help="Path to g++")
45parser.add_argument(
46    "--verbose",
47    "-v",
48    action="store_true",
49    help="Display verbose output, including commands executed",
50)
51parser.add_argument(
52    "tests",
53    nargs="*",
54    default=["sixgill-tree", "suppression", "hazards", "exceptions", "virtual"],
55    help="tests to run",
56)
57
58cfg = parser.parse_args()
59
60if not cfg.js:
61    exit("Must specify JS binary through environment variable or --js option")
62if not cfg.cc:
63    if cfg.gccdir:
64        cfg.cc = os.path.join(cfg.gccdir, "bin", "gcc")
65    else:
66        cfg.cc = "gcc"
67if not cfg.cxx:
68    if cfg.gccdir:
69        cfg.cxx = os.path.join(cfg.gccdir, "bin", "g++")
70    else:
71        cfg.cxx = "g++"
72if not cfg.sixgill_bin:
73    cfg.sixgill_bin = os.path.join(cfg.sixgill, "usr", "bin")
74if not cfg.sixgill_plugin:
75    cfg.sixgill_plugin = os.path.join(
76        cfg.sixgill, "usr", "libexec", "sixgill", "gcc", "xgill.so"
77    )
78
79subprocess.check_call(
80    [cfg.js, "-e", 'if (!getBuildConfiguration()["has-ctypes"]) quit(1)']
81)
82
83
84def binpath(prog):
85    return os.path.join(cfg.sixgill_bin, prog)
86
87
88def make_dir(dirname, exist_ok=True):
89    try:
90        os.mkdir(dirname)
91    except OSError as e:
92        if exist_ok and e.strerror == "File exists":
93            pass
94        else:
95            raise
96
97
98outroot = os.path.join(testdir, "out")
99make_dir(outroot)
100
101failed = set()
102for name in cfg.tests:
103    name = os.path.basename(name)
104    indir = os.path.join(testdir, name)
105    outdir = os.path.join(outroot, name)
106    make_dir(outdir)
107
108    test = Test(indir, outdir, cfg, verbose=cfg.verbose)
109
110    os.chdir(outdir)
111    for xdb in glob("*.xdb"):
112        os.unlink(xdb)
113    print("START TEST {}".format(name), flush=True)
114    testpath = os.path.join(indir, "test.py")
115    testscript = open(testpath).read()
116    testcode = compile(testscript, testpath, "exec")
117    try:
118        exec(testcode, {"test": test, "equal": equal})
119    except subprocess.CalledProcessError:
120        print("TEST-FAILED: %s" % name)
121        failed.add(name)
122    except AssertionError:
123        print("TEST-FAILED: %s" % name)
124        failed.add(name)
125        raise
126    else:
127        print("TEST-PASSED: %s" % name)
128
129if failed:
130    raise Exception("Failed tests: " + " ".join(failed))
131