mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 08:41:13 -07:00
The remaining bench diagnostics run from forgetest's #bench tab. The tools that also run from a LAN host share scripts/bench/gfbench.py: GF_HOST names a remote machine (host mode, sysfs through ssh, Grbl and forgectrl over the LAN); unset, the tool runs on the board itself (local mode, sysfs directly, everything on 127.0.0.1), which is how the page runs them - with GF_HOST=127.0.0.1, the panel token in GF_TOKEN and their data files under <data>/bench/ (FORGETEST_BENCH_DATA). The helper also reads a machine setting from forgectrl, or from the settings file on the board while forgectrl is stopped. Ported: pwm_sweep / pwm_hold (scope = a takeover; the latch relocked, the write refused if FIRE or LASER_ON reads active), pwm_stream_test (PASS/FAIL exit), flow_characterize, flow_recheck_char, flow_warm_validate and flow_matrix (takeovers: forgectrl owns the thermal hardware, so the page's takeover replaces the tools' own controller stop/restart, whose command line predated the supervisor; results and logs in the bench data directory), flow_sustained, fan_test, temp_calibrate (dry; watch bounded in seconds; the threshold and the coolant conversion from the shared code), flow_escalate_drill (cool_confirm_max_s shortened through forgectrl's settings for the drill and restored; the setting's minimum is the default budget), and live_fire_drills (<drill> [S] [F], all six drills, host from GF_HOST, token from the board). flow_matrix joins the registry. What stays unported cannot run against the machine at all: the two null-sink CI harnesses and the .puls decoder. Runner: a scope tool runs inside the takeover wrapper; the bench environment above is passed to every tool. Tests: test_bench_registry (registry <-> scripts/bench consistency, every ported tool builds its command line, every script compiles, gfbench host/local modes) and the server test (scope tool takeover, the environment reaching the tool). Local mode smoke-run on the bench (temp_calibrate watch, setting, token) from /tmp, removed after. No catalog consequence: bench tools are not image components (dev-only forgetest); the acceptance catalog is unchanged.
115 lines
4.9 KiB
Python
115 lines
4.9 KiB
Python
"""The bench registry against scripts/bench: every python tool is
|
|
registered (or named as not-a-tool), every registered script exists,
|
|
every argument spec builds a command line, every script compiles, and
|
|
the shared helper resolves host and local mode as documented."""
|
|
import glob
|
|
import os
|
|
import py_compile
|
|
import subprocess
|
|
import sys
|
|
import unittest
|
|
|
|
import helpers # noqa: F401 (sys.path)
|
|
from forgetest import bench as bench_mod
|
|
|
|
REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
BENCH = os.path.join(REPO, "scripts", "bench")
|
|
|
|
|
|
class RegistryTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.bench = bench_mod.Bench(tool_dir=BENCH, index_path=os.devnull)
|
|
self.scripts = sorted(os.path.basename(p) for p in glob.glob(os.path.join(BENCH, "*.py")))
|
|
|
|
def test_every_python_tool_is_registered(self):
|
|
registered = {t["script"] for t in bench_mod.TOOLS}
|
|
missing = [s for s in self.scripts if s not in registered and s not in bench_mod.NOT_TOOLS]
|
|
self.assertEqual(missing, [], "scripts/bench python files missing from the registry")
|
|
|
|
def test_every_registered_script_exists(self):
|
|
for t in bench_mod.TOOLS:
|
|
self.assertTrue(os.path.exists(os.path.join(BENCH, t["script"])), t["script"])
|
|
|
|
def test_ids_unique_and_fields_valid(self):
|
|
ids = [t["id"] for t in bench_mod.TOOLS]
|
|
self.assertEqual(len(ids), len(set(ids)))
|
|
for t in bench_mod.TOOLS:
|
|
self.assertIn(t["safety"], ("dry", "takeover", "scope", "live"), t["id"])
|
|
self.assertIn(t["where"], ("board", "host"), t["id"])
|
|
self.assertIsInstance(t["ported"], bool, t["id"])
|
|
self.assertTrue(t["desc"], t["id"])
|
|
for a in t.get("args", []):
|
|
self.assertIn(a["type"], ("str", "int", "float", "choice"), (t["id"], a["name"]))
|
|
if a["type"] == "choice":
|
|
self.assertIn(a["default"], a["choices"], (t["id"], a["name"]))
|
|
|
|
def test_ported_tools_build_a_command_with_defaults(self):
|
|
for t in bench_mod.TOOLS:
|
|
if not t["ported"]:
|
|
continue
|
|
ok, argv, err = self.bench.command(t, {})
|
|
self.assertTrue(ok, "%s: %s" % (t["id"], err))
|
|
self.assertEqual(argv[1], os.path.join(BENCH, t["script"]))
|
|
|
|
def test_unported_tools_are_host_only(self):
|
|
# what stays unported is what cannot run on the machine at all
|
|
for t in bench_mod.TOOLS:
|
|
if not t["ported"]:
|
|
self.assertEqual(t["where"], "host", t["id"])
|
|
self.assertIn("not a bench-page tool", t["desc"], t["id"])
|
|
|
|
def test_scripts_compile(self):
|
|
for s in self.scripts:
|
|
py_compile.compile(os.path.join(BENCH, s), doraise=True)
|
|
|
|
|
|
class GfbenchTests(unittest.TestCase):
|
|
"""gfbench.py in a subprocess (module-level host resolution)."""
|
|
|
|
def run_snippet(self, code, env_extra):
|
|
env = dict(os.environ)
|
|
env.pop("GF_HOST", None)
|
|
env.pop("FORGETEST_BENCH_DATA", None)
|
|
env.update(env_extra)
|
|
r = subprocess.run([sys.executable, "-c", code], cwd=BENCH, env=env,
|
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, timeout=30)
|
|
return r.returncode, r.stdout.strip()
|
|
|
|
def test_host_mode(self):
|
|
rc, out = self.run_snippet("import gfbench; print(gfbench.HOST, gfbench.LOCAL, gfbench.SSH[0])",
|
|
{"GF_HOST": "192.0.2.7", "GF_SSH": "fakessh -x"})
|
|
self.assertEqual(rc, 0, out)
|
|
self.assertEqual(out, "192.0.2.7 False fakessh")
|
|
|
|
def test_local_when_named_local(self):
|
|
rc, out = self.run_snippet("import gfbench; print(gfbench.HOST, gfbench.LOCAL); "
|
|
"print(gfbench.board('echo hi').strip())",
|
|
{"GF_HOST": "127.0.0.1"})
|
|
self.assertEqual(rc, 0, out)
|
|
self.assertEqual(out.splitlines(), ["127.0.0.1 True", "hi"])
|
|
|
|
def test_refuses_without_a_machine(self):
|
|
if os.path.isdir("/sys/glowforge"):
|
|
self.skipTest("running on the machine")
|
|
rc, out = self.run_snippet("import gfbench", {})
|
|
self.assertNotEqual(rc, 0)
|
|
self.assertIn("GF_HOST", out)
|
|
|
|
def test_degc_and_data_dir(self):
|
|
import tempfile
|
|
d = tempfile.mkdtemp(prefix="forgetest-bench-")
|
|
rc, out = self.run_snippet(
|
|
"import gfbench, os; print(round(gfbench.degc(700), 2), gfbench.degc('x') != gfbench.degc('x'), "
|
|
"os.path.dirname(gfbench.data_path('f.json')) == os.environ['FORGETEST_BENCH_DATA'])",
|
|
{"GF_HOST": "127.0.0.1", "FORGETEST_BENCH_DATA": d})
|
|
self.assertEqual(rc, 0, out)
|
|
val, nan, indir = out.split()
|
|
# a plausible coolant reading (700 counts is room temperature territory)
|
|
self.assertTrue(15.0 < float(val) < 35.0, val)
|
|
self.assertEqual(nan, "True")
|
|
self.assertEqual(indir, "True")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|