about summary refs log tree commit diff
path: root/modules/nixos-hardware/tests/run.py
diff options
context:
space:
mode:
Diffstat (limited to 'modules/nixos-hardware/tests/run.py')
-rwxr-xr-xmodules/nixos-hardware/tests/run.py102
1 files changed, 102 insertions, 0 deletions
diff --git a/modules/nixos-hardware/tests/run.py b/modules/nixos-hardware/tests/run.py
new file mode 100755
index 000000000000..653337d19190
--- /dev/null
+++ b/modules/nixos-hardware/tests/run.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -p nix -p python3 -i python
+
+import argparse
+import multiprocessing
+import re
+import subprocess
+import sys
+from pathlib import Path
+from typing import List, Tuple
+
+TEST_ROOT = Path(__file__).resolve().parent
+ROOT = TEST_ROOT.parent
+
+GREEN = "\033[92m"
+RED = "\033[91m"
+RESET = "\033[0m"
+
+
+def parse_readme() -> List[str]:
+    profiles = set()
+    with open(ROOT.joinpath("README.md")) as f:
+        for line in f:
+            results = re.findall(r"<nixos-hardware/[^>]+>", line)
+            profiles.update(results)
+    return list(profiles)
+
+
+def build_profile(profile: str) -> Tuple[str, subprocess.CompletedProcess]:
+    # Hard-code this for now until we have enough other architectures to care about this.
+    system = "x86_64-linux"
+    if "raspberry-pi/2" in profile:
+        system = "armv7l-linux"
+
+    cmd = [
+        "nix-build",
+        "-I",
+        f"nixos-hardware={ROOT}",
+        "--dry-run",
+        "--show-trace",
+        "build-profile.nix",
+        "--system",
+        system,
+        "--arg",
+        "profile",
+        profile,
+    ]
+    res = subprocess.run(
+        cmd, cwd=TEST_ROOT, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
+    )
+    return (profile, res)
+
+
+def parse_args() -> argparse.Namespace:
+    parser = argparse.ArgumentParser(description="Run hardware tests")
+    parser.add_argument(
+        "--jobs",
+        type=int,
+        default=multiprocessing.cpu_count(),
+        help="Number of parallel evaluations."
+        "If set to 1 it disable multi processing (suitable for debugging)",
+    )
+    parser.add_argument("profiles", nargs="*")
+    return parser.parse_args()
+
+
+def main() -> None:
+    args = parse_args()
+    if len(args.profiles) == 0:
+        profiles = parse_readme()
+    else:
+        profiles = args.profiles
+
+    failed_profiles = []
+
+    def eval_finished(args: Tuple[str, subprocess.CompletedProcess]) -> None:
+        profile, res = args
+        if res.returncode == 0:
+            print(f"{GREEN}OK {profile}{RESET}")
+        else:
+            print(f"{RED}FAIL {profile}:{RESET}", file=sys.stderr)
+            if res.stdout != "":
+                print(f"{RED}{res.stdout.rstrip()}{RESET}", file=sys.stderr)
+            print(f"{RED}{res.stderr.rstrip()}{RESET}", file=sys.stderr)
+            failed_profiles.append(profile)
+
+    if len(profiles) == 0 or args.jobs == 1:
+        for profile in profiles:
+            eval_finished(build_profile(profile))
+    else:
+        pool = multiprocessing.Pool(processes=args.jobs)
+        for r in pool.imap(build_profile, profiles):
+            eval_finished(r)
+    if len(failed_profiles) > 0:
+        print(f"\n{RED}The following {len(failed_profiles)} test(s) failed:{RESET}")
+        for profile in failed_profiles:
+            print(f"{sys.argv[0]} '{profile}'")
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()