aboutsummaryrefslogtreecommitdiff
path: root/salis.py
diff options
context:
space:
mode:
Diffstat (limited to 'salis.py')
-rwxr-xr-xsalis.py51
1 files changed, 33 insertions, 18 deletions
diff --git a/salis.py b/salis.py
index f247426..f86cd2e 100755
--- a/salis.py
+++ b/salis.py
@@ -2,6 +2,7 @@
# index
# [section] argument parser
+# [section] run unit tests
# [section] build class
# [section] logging
# [section] options dict formatter
@@ -20,6 +21,7 @@ import shutil
import socket
import subprocess
import sys
+import unittest
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, ArgumentTypeError, RawTextHelpFormatter
from tempfile import TemporaryDirectory
@@ -39,9 +41,7 @@ new = sub_parsers.add_parser("new", formatter_class=formatter_class, help="creat
load = sub_parsers.add_parser("load", formatter_class=formatter_class, help="load saved simulation")
server = sub_parsers.add_parser("server", formatter_class=formatter_class, help="run data server")
client = sub_parsers.add_parser("client", formatter_class=formatter_class, help="run data client")
-
-architectures = os.listdir("./arch")
-uis = os.listdir("./ui")
+test = sub_parsers.add_parser("test", formatter_class=formatter_class, help="run unit tests")
def seed(i):
ival = int(i, 0)
@@ -67,14 +67,14 @@ fmt_id = lambda val: val
fmt_hex = lambda val: hex(int(val, 0)) if type(val) == str else hex(val)
options = {
- (("A", "anc"), (new,), fmt_id): {"metavar": "ANC", "help": "ancestor file name without extension; to be compiled on all cores; ANC points to 'anc/{arch}/{ANC}.asm'", "required": True, "type": str},
- (("a", "arch"), (new,), fmt_id): {"choices": architectures, "help": "VM architecture", "default": "dummy", "required": False, "type": str},
+ (("a", "anc"), (new,), fmt_id): {"metavar": "ANC", "help": "ancestor file name without extension; to be compiled on all cores; ANC points to '{anc-path}/{arch}/{ANC}.asm'", "required": True, "type": str},
+ (("A", "anc-path"), (new,), fmt_id): {"metavar": "PATH", "help": "path to search for ancestor files", "default": "anc", "required": False, "type": str},
+ (("b", "build-only"), (new, load, server, client), fmt_id): {"action": "store_true", "help": "only build (do not run) executable", "required": False},
(("C", "clones"), (new,), fmt_id): {"metavar": "N", "help": "number of ancestor clones on each core", "default": 1, "required": False, "type": nat},
(("c", "cores"), (new,), fmt_id): {"metavar": "N", "help": "number of simulator cores", "default": 2, "required": False, "type": nat},
(("d", "data-push-pow"), (new,), fmt_id): {"metavar": "POW", "help": "data aggregation interval exponent; interval = 2^{POW} >= {sync-pow}", "default": 28, "required": False, "type": nat},
- (("F", "font"), (client,), fmt_id): {"metavar": "FILE", "help": "font face to use", "default": "/usr/share/fonts/droid/DroidSansMono.ttf", "required": False},
+ (("f", "font"), (client,), fmt_id): {"metavar": "FILE", "help": "font face to use", "default": "/usr/share/fonts/droid/DroidSansMono.ttf", "required": False},
(("f", "force"), (new,), fmt_id): {"action": "store_true", "help": "overwrite existing simulation of given name", "required": False},
- (("F", "muta-flip"), (new,), fmt_id): {"action": "store_true", "help": "cosmic rays flip bits instead of randomizing whole bytes", "required": False},
(("g", "c-compiler"), (new, load, server, client), fmt_id): {"metavar": "CC", "help": "C compiler to use", "default": "gcc", "required": False, "type": str},
(("G", "c-compiler-flags"), (new, load, server, client), fmt_id): {"metavar": "FLAGS", "help": "base set of flags to pass to C compiler", "default": "-Wall -Wextra -Werror -Wno-overlength-strings -pedantic", "required": False, "type": str},
(("H", "home"), (new, load, server), fmt_id): {"metavar": "PATH", "help": "salis home directory", "default": os.path.join(os.environ["HOME"], ".salis"), "required": False, "type": str},
@@ -83,13 +83,16 @@ options = {
(("m", "mvec-pow"), (new,), fmt_id): {"metavar": "POW", "help": "memory core size exponent; size = 2^{POW}", "default": 20, "required": False, "type": pos},
(("n", "name"), (new, load, server), fmt_id): {"metavar": "NAME", "help": "name of new or loaded simulation", "default": "def.sim", "required": False, "type": str},
(("o", "optimized"), (new, load, server, client), fmt_id): {"action": "store_true", "help": "build with optimizations", "required": False},
+ (("p", "pattern"), (test,), fmt_id): {"metavar": "PATTERN", "help": "pattern for unit test discovery", "default": "*", "required": False, "type": str},
(("P", "port"), (server, client), fmt_id): {"metavar": "PORT", "help": "port number for data server", "default": 8080, "required": False, "type": port},
(("p", "pre-cmd"), (new, load, server, client), fmt_id): {"metavar": "CMD", "help": "shell command with which to wrap call to executable; e.g. gdb, time, valgrind, etc.", "required": False, "type": str},
(("s", "seed"), (new,), fmt_hex): {"metavar": "SEED", "help": "seed value for new simulation; a value of 0 disables cosmic rays; a value of -1 creates a random seed", "default": 0, "required": False, "type": seed},
(("T", "keep-temp-dir"), (new, load, server, client), fmt_id): {"action": "store_true", "help": "keep temporary directory on exit", "required": False},
(("t", "thread-gap"), (new, load), fmt_hex): {"metavar": "N", "help": "memory gap between core elements in bytes; may help reduce cache misses", "default": 0x100, "required": False, "type": nat},
- (("u", "ui"), (new, load), fmt_id): {"choices": uis, "help": "user interface", "default": "curses", "required": False, "type": str},
+ (("u", "ui"), (new, load), fmt_id): {"metavar": "UI", "help": "user interface", "default": "curses", "required": False, "type": str},
+ (("U", "ui-path"), (new, load), fmt_id): {"metavar": "PATH", "help": "path to search for UIs", "default": "ui", "required": False, "type": str},
(("v", "no-vsync"), (client,), fmt_id): {"action": "store_true", "help": "disable vsync", "required": False},
+ (("v", "vm-arch"), (new,), fmt_id): {"metavar": "ARCH", "help": "VM architecture", "default": "dummy", "required": False, "type": str},
(("x", "cpp-compiler"), (client,), fmt_id): {"metavar": "CXX", "help": "C++ compiler to use", "default": "g++", "required": False, "type": str},
(("X", "cpp-compiler-flags"), (client,), fmt_id): {"metavar": "FLAGS", "help": "base set of flags to pass to C++ compiler", "default": "-Wall -Wextra -Werror -pedantic -std=c++20 -lstdc++", "required": False, "type": str},
(("y", "sync-pow"), (new,), fmt_id): {"metavar": "POW", "help": "core sync interval exponent; sync events occur every N steps, where N = 2^{POW}", "default": 20, "required": False, "type": pos},
@@ -103,6 +106,16 @@ for (name, sub_parsers, _), kwargs in options.items():
args = parser.parse_args()
# ------------------------------------------------------------------------------
+# [section] run unit tests
+# ------------------------------------------------------------------------------
+if args.command == "test":
+ test_runner = unittest.TextTestRunner(verbosity=2)
+ test_loader = unittest.TestLoader()
+ test_loader.testNamePatterns = [args.pattern]
+ test_runner.run(test_loader.discover("test"))
+ sys.exit(0)
+
+# ------------------------------------------------------------------------------
# [section] build class
# ------------------------------------------------------------------------------
class Build:
@@ -296,14 +309,14 @@ if args.command == "client":
# ------------------------------------------------------------------------------
# [section] source architecture variables
# ------------------------------------------------------------------------------
-arch_path = os.path.join("arch", args.arch, "vars.py")
+arch_path = os.path.join("arch", args.vm_arch, "vars.py")
log.info(f"Loading architecture variables from: {arch_path}")
arch_vars = runpy.run_path(arch_path, init_globals=globals())
# ------------------------------------------------------------------------------
# [section] ancestor assembler
# ------------------------------------------------------------------------------
-anc_path = os.path.join("anc", args.arch, f"{args.anc}.asm")
+anc_path = os.path.join(args.anc_path, args.vm_arch, f"{args.anc}.asm")
if not os.path.isfile(anc_path):
raise RuntimeError(f"Could not find ancestor file: {anc_path}")
@@ -337,11 +350,11 @@ log.info(f"Compiled ancestor file '{anc_path}' into byte array: {anc_repr}")
# [section] compiler flags
# ------------------------------------------------------------------------------
def pop_ui_vars():
- ns.ui_path = os.path.join("ui", args.ui, "vars.py")
- ns.ui_vars = runpy.run_path(ns.ui_path, init_globals=globals())
- log.info(f"Sourced UI variables from: {ns.ui_path}")
+ ns.ui_vars_path = os.path.join(args.ui_path, args.ui, "vars.py")
+ ns.ui_vars = runpy.run_path(ns.ui_vars_path, init_globals=globals())
+ log.info(f"Sourced UI variables from: {ns.ui_vars_path}")
- ns.b.flags.add(f"-Iui/{args.ui}")
+ ns.b.flags.add(f"-I{args.ui_path}/{args.ui}")
ns.b.flags.update({*ns.ui_vars["flags"]})
ns.b.defines.update({*ns.ui_vars["defines"]})
ns.b.links.update({*ns.ui_vars["links"]})
@@ -362,10 +375,10 @@ def pop_net_vars():
ns.b.links.add("-ljson-c")
def pop_general():
- ns.b.flags.add(f"-Iarch/{args.arch}")
+ ns.b.flags.add(f"-Iarch/{args.vm_arch}")
ns.b.flags.add("-Icore")
- if args.muta_flip:
+ if arch_vars["muta_flip"]:
ns.b.defines.add("-DMUTA_FLIP")
if arch_vars["mvec_loop"]:
@@ -374,7 +387,7 @@ def pop_general():
ns.b.defines.add(f"-DANC=\"{args.anc}\"")
ns.b.defines.add(f"-DANC_BYTES={anc_repr}")
ns.b.defines.add(f"-DANC_SIZE={len(anc_bytes)}")
- ns.b.defines.add(f"-DARCH=\"{args.arch}\"")
+ ns.b.defines.add(f"-DARCH=\"{args.vm_arch}\"")
ns.b.defines.add(f"-DAUTOSAVE_INTERVAL={2 ** args.auto_save_pow}ul")
ns.b.defines.add(f"-DCLONES={args.clones}")
ns.b.defines.add(f"-DCOMMAND_{args.command.upper()}")
@@ -445,4 +458,6 @@ if args.command == "client":
# [section] build and launch
# ------------------------------------------------------------------------------
ns.b.build()
-ns.b.exec()
+
+if not args.build_only:
+ ns.b.exec()