From 766ce6ff1ea584dfe980a5e0b29251768c4c0726 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 21 Oct 2026 11:36:01 +0200 Subject: sbb-remote 0.12.0: named builds, per-package job results, prune - Packages: named packages run slackrepo build, which also updates outdated ones; update on a named item finds only already-built packages. The per-row Update action is gone, Update all stays. - A real slackrepo job reads its items' buildresults rows into the job's results and is failed when any item failed, aborted or was bad: slackrepo itself exits 0. The notification names the failed items; a Build results dialog lists every item with its log and Retry failed, reopenable from the Jobs ribbon. - slackrepo prune: the helper lists built items whose SlackBuild is gone and runs slackrepo remove on them, without an Update all. stats gives the count; Packages gets a Prune action, the Built tile a 'to prune' note. Remove and prune ask for confirmation at Run it. Co-Authored-By: Claude Opus 5.5 --- tests/test_gui.py | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'tests/test_gui.py') diff --git a/tests/test_gui.py b/tests/test_gui.py index ca415d3..f5894de 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -468,6 +468,29 @@ class SmokeTest(unittest.TestCase): self.assertIn("…", text) self.assertNotIn("pkg " * 40, text) + def test_failed_build_shows_results(self): + job = dict(JOB, id="20260101-000002-slackrepo", kind="slackrepo", args=["build", "a", "b", "c"]) + w, remote = self.window(jobs=({"jobs": [dict(job, status="running")]}, None), + **{"job-start": ({"id": "20260101-000003-slackrepo"}, None)}) + w.mine.add(job["id"]) + w.poll_jobs() + results = {"cat/a": "ok", "cat/b": "failed", "cat/c": "aborted", "d": "bad"} + remote.answers["jobs"] = ({"jobs": [dict(job, status="failed", end=125, exit=0, + results=results)]}, None) + w.poll_jobs() + self.assertEqual(w.notifier.sent[-1][1], + "Failed: cat/b, cat/c, d (1 ok, 1 failed, 1 aborted, 1 bad) after 2m 5s") + d = w.next_steps[job["id"]] + self.assertEqual(d.list.item(0).text(), "cat/b failed") + d.retry_btn.click() + self.assertIn(("job-start", "slackrepo", "build", "--preview", "b", "c"), remote.calls) + + def test_prunable_enables_prune(self): + w, _ = self.window(stats=(dict(STATS, prunable=3, slackrepo={"results": {}, "built": 5}), None)) + w.poll_jobs() + self.assertTrue(w.packages_tab.act_prune.isEnabled()) + self.assertIn("3 to prune", w.dashboard.tiles["Built"].detail.text()) + def test_slackpkg_dot(self): old = dict(STATS, slackpkg={"update": None, "upgrade": 1}) w, _ = self.window(stats=(old, None)) @@ -500,7 +523,7 @@ class SmokeTest(unittest.TestCase): tab.chips["orphaned"].setChecked(False) started = [] tab.start_requested.connect(started.append) - tab.act("update") + tab.act("build") self.assertEqual(started, []) # nothing checked, no current row tab.table.clicked.emit(tab.proxy.index(3, 2)) # a click on a row's values ticks it self.assertEqual(tab.model.checked, {"curl"}) @@ -508,10 +531,10 @@ class SmokeTest(unittest.TestCase): self.assertEqual(tab.model.checked, set()) tab.table.setCurrentIndex(tab.proxy.index(1, 1)) self.assertIn("x", tab.detail.text()) - tab.act("update") # highlighted but not ticked: nothing + tab.act("build") # highlighted but not ticked: nothing self.assertEqual(started, []) tab.model.checked.add("foo") - tab.act("update") + tab.act("build") tab.model.checked.clear() select, clear = tab.keys select.activated.emit() @@ -521,9 +544,12 @@ class SmokeTest(unittest.TestCase): tab.model.checked.update({"nuclei", "curl"}) tab.act("build") tab.update_all() - self.assertEqual(started, [["slackrepo", "update", "--preview", "foo"], + tab.set_prunable(2) + tab.act_prune.trigger() + self.assertEqual(started, [["slackrepo", "build", "--preview", "foo"], ["slackrepo", "build", "--preview", "curl", "nuclei"], - ["slackrepo", "update", "--preview"]]) + ["slackrepo", "update", "--preview"], + ["slackrepo", "prune", "--preview"]]) def end_preview(self, job, **answers): """A window where job, started by this session, goes from running to its status.""" @@ -717,13 +743,14 @@ class SmokeTest(unittest.TestCase): self.assertNotIn(("inventory",), remote.calls) # each tab loads on first show expect = { "Dashboard": (["Refresh"], []), - "Packages": (["Build", "Update", "Rebuild", "Remove", "Update all", "New hint", "Publish"], + "Packages": (["Build", "Rebuild", "Remove", "Update all", "Prune", "New hint", "Publish"], ["Select visible", "Clear", "Refresh"]), "Hints": (["Check upstream", "Update selected", "Delete hint", "Phantom deps…"], ["Select visible", "Clear", "No download", "Refresh"]), "Results": (["Show build log"], ["Refresh"]), "Repository": ([], ["Refresh"]), - "Jobs": (["Pull overlay", "Check shadowing", "Rebuild tree", "Run it", "Kill"], []), + "Jobs": (["Pull overlay", "Check shadowing", "Rebuild tree", "Run it", "Build results", "Kill"], + []), } for i in range(w.tabs.count()): w.tabs.setCurrentIndex(i) @@ -742,6 +769,8 @@ class SmokeTest(unittest.TestCase): p, h, j = w.open_packages(), w.open_hints(), w.open_jobs() for a, why in ((p.act_build["build"], "tick packages first"), (p.act_new_hint, "none of the ticked packages is without a hint"), + (p.act_prune, "no built package lost its SlackBuild"), + (j.act_results, "select a build with failed packages"), (h.act_update, "tick outdated packages after Check upstream"), (h.act_delete, "none of the ticked packages has a hint"), (j.act_run, "select a finished preview"), (j.act_kill, "select a running job")): @@ -872,7 +901,7 @@ class SmokeTest(unittest.TestCase): self.assertEqual(w.hint_dialogs, []) w.show_hint_result({"ok": ["nuclei"], "failed": []}) w.hint_dialogs[0].build() - self.assertIn(("job-start", "slackrepo", "update", "--preview", "nuclei"), remote.calls) + self.assertIn(("job-start", "slackrepo", "build", "--preview", "nuclei"), remote.calls) def test_clear_with_filter_and_current_row(self): # regression: Clear emitted layoutChanged with no layoutAboutToBeChanged; -- cgit v1.2.3