Download .github/scripts/run_gui_tests.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 5.46 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/.github/scripts/run_gui_tests.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/.github/scripts/run_gui_tests.py
-
curl -L -o run_gui_tests.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/.github/scripts/run_gui_tests.py
5.46 kB
| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| """ | |
| run_gui_tests.py | |
| List registered tests via `FreeCAD -t`, filter for GUI tests (names containing 'Gui'), and run each | |
| GUI test module using the specified FreeCAD executable. | |
| Usage: | |
| run_gui_tests.py [FREECAD_EXEC] | |
| If FREECAD_EXEC is omitted the script falls back to 'FreeCAD' on PATH. | |
| If FREECAD_EXEC is a directory containing bin/FreeCAD, that binary is used. | |
| If FREECAD_EXEC is an executable path, it is used directly. | |
| This script returns 0 if all GUI modules run successfully. Otherwise it returns the last non-zero | |
| exit code. | |
| """ | |
| from __future__ import annotations | |
| import sys | |
| import subprocess | |
| import os | |
| from pathlib import Path | |
| def find_executable(arg: str | None) -> str: | |
| """Return the FreeCAD executable path to use. | |
| If `arg` is None or empty, returns the plain name 'FreeCAD' which will be looked up on PATH. If | |
| `arg` is a directory and contains `bin/FreeCAD` that binary will be returned. If `arg` is a file | |
| path it is returned as-is. Otherwise the original argument is returned. | |
| Common use cases: use the FreeCAD binary from a build directory or an installed FreeCAD prefix. | |
| """ | |
| if not arg: | |
| return "FreeCAD" | |
| p = Path(arg) | |
| if p.is_dir(): | |
| candidate = p / "bin" / "FreeCAD" | |
| if candidate.exists(): | |
| return str(candidate) | |
| if p.is_file(): | |
| return str(p) | |
| # fallback: return as-is (may be on PATH) | |
| return arg | |
| def validate_executable(path: str) -> tuple[bool, str]: | |
| """Return (ok, message). Checks if the executable exists or is likely on PATH. | |
| This is best effort: if a bare name is given (e.g. 'FreeCAD') we can't stat it here, so we | |
| accept it but warn. If the path points to a file, we check executability. Also warn if the name | |
| looks like the CLI-only 'FreeCADCmd'. | |
| """ | |
| p = Path(path) | |
| if p.is_file(): | |
| if not os.access(str(p), os.X_OK): | |
| return False, f"File exists but is not executable: {path}" | |
| if p.name.endswith("FreeCADCmd"): | |
| return ( | |
| True, | |
| ( | |
| "Warning: executable looks like 'FreeCADCmd' (CLI); GUI tests require the GUI " | |
| "binary 'FreeCAD'." | |
| ), | |
| ) | |
| return True, "" | |
| # Bare name or non-existent path: accept but warn | |
| if p.name.endswith("FreeCADCmd"): | |
| return ( | |
| True, | |
| ( | |
| "Warning: executable name looks like 'FreeCADCmd' (CLI); GUI tests require the GUI " | |
| "binary 'FreeCAD'." | |
| ), | |
| ) | |
| return True, "" | |
| def run_and_capture(cmd: list[str]) -> tuple[int, str]: | |
| """Run `cmd` and return (returncode, combined stdout+stderr string). | |
| If the executable is not found a return code of 127 is returned and a small error string is | |
| provided as output to aid diagnosis. | |
| """ | |
| try: | |
| proc = subprocess.run( | |
| cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False | |
| ) | |
| return proc.returncode, proc.stdout | |
| except FileNotFoundError: | |
| return 127, f"Executable not found: {cmd[0]}\n" | |
| def parse_registered_tests(output: str) -> list[str]: | |
| """Parse output from `FreeCAD -t` and return a list of registered test unit names. | |
| The function looks for the section starting with the literal 'Registered test units:' and | |
| then collects non-empty, stripped lines from that point onwards as test names. | |
| """ | |
| lines = output.splitlines() | |
| tests: list[str] = [] | |
| started = False | |
| for ln in lines: | |
| if not started: | |
| if "Registered test units:" in ln: | |
| started = True | |
| continue | |
| s = ln.strip() | |
| if not s: | |
| # allow blank lines but keep going | |
| continue | |
| tests.append(s) | |
| return tests | |
| def main(argv: list[str]) -> int: | |
| """Entry point: run GUI test modules registered in the FreeCAD executable. | |
| Returns the last non-zero exit code from any GUI test module, or 0 on success. | |
| """ | |
| exec_arg = argv[1] if len(argv) > 1 else None | |
| freecad_exec = find_executable(exec_arg) | |
| print(f"Using FreeCAD executable: {freecad_exec}") | |
| ok, msg = validate_executable(freecad_exec) | |
| if msg: | |
| print(msg, file=sys.stderr) | |
| if not ok: | |
| print(f"Aborting: invalid FreeCAD executable: {freecad_exec}", file=sys.stderr) | |
| return 3 | |
| code, out = run_and_capture([freecad_exec, "-t"]) | |
| if code != 0: | |
| print( | |
| f"Warning: listing tests returned exit code {code}; attempting to parse output anyway", | |
| file=sys.stderr, | |
| ) | |
| tests = parse_registered_tests(out) | |
| if not tests: | |
| print("No registered tests found; exiting with success.") | |
| return 0 | |
| gui_tests = [t for t in tests if "Gui" in t] | |
| if not gui_tests: | |
| print("No GUI tests found in registered tests; nothing to run.") | |
| return 0 | |
| print("Found GUI test modules:") | |
| for t in gui_tests: | |
| print(" ", t) | |
| last_rc = 0 | |
| for mod in gui_tests: | |
| print(f"\nRunning GUI tests for module: {mod}") | |
| rc, out = run_and_capture([freecad_exec, "-t", mod]) | |
| print(out) | |
| if rc != 0: | |
| print(f"Module {mod} exited with code {rc}", file=sys.stderr) | |
| last_rc = rc | |
| return last_rc | |
| if __name__ == "__main__": | |
| sys.exit(main(sys.argv)) | |