aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/assistant/test_fileops.py
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-16 16:52:45 +0200
committerDanilo M. <danix@danix.xyz>2026-10-16 16:52:45 +0200
commitbb9442a07cebe70c3492cca31f31a8270bb83134 (patch)
tree2dff25cd4c3715b5b54d4c65ee6619e896f170aa /desktop/assistant/test_fileops.py
parent193cd8c2e1cc7e16b1389b95036bf7f4f99f97e0 (diff)
downloadquickshell-bb9442a07cebe70c3492cca31f31a8270bb83134.tar.gz
quickshell-bb9442a07cebe70c3492cca31f31a8270bb83134.zip
fix(assistant): review of the Do row and fileops
The canvas: a To .md ended any other way than its own Stop (the footer Stop, a question, a drop, a daemon restart, an older daemon refusing the command) left the card at Working forever. The footer Stop goes through stopOp(), asking and dropping end the operation first, a disconnect ends it, and an error while nothing else runs is taken as its outcome. Each fileops run gets its own Process, so a stopped run's late answer is never read as the next one's. To .md takes one PDF, as one file is written. Compress... no longer flips back when file(1) refines a kind, long errors are elided, and a desktop with no documents folder gets Documents/Assistant rather than ~/Assistant. fileops: an animated GIF to PNG or JPEG left one file per frame under names the rename never found; the first frame is taken. JPEG flattens transparency onto white. Zipping a folder holding the output folder read its own growing archive without end; only regular files outside it are added, which also skips FIFOs and dangling links. qpdf warnings no longer fail a merge. A folder's free name keeps its dots, a lone .gz or .xz is decompressed rather than refused, and a dotfile's archive is not hidden. Tools die with fileops even when it is killed outright, a killed run's leftovers are swept on the next run, a stop between fork and bookkeeping cannot miss the child, and a late stop cannot add a second answer. Error lines lose bsdtar's doubled prefix and its delayed-exit noise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/assistant/test_fileops.py')
-rwxr-xr-xdesktop/assistant/test_fileops.py41
1 files changed, 41 insertions, 0 deletions
diff --git a/desktop/assistant/test_fileops.py b/desktop/assistant/test_fileops.py
index 850d1d7..8fd8449 100755
--- a/desktop/assistant/test_fileops.py
+++ b/desktop/assistant/test_fileops.py
@@ -122,6 +122,47 @@ with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as out:
r, _ = fileops("audio", out, g)
assert not r["ok"] and "audio" in r["error"], r # a GIF has no audio track
+ # From review: each of these failed or misbehaved once.
+ anim = os.path.join(src, "anim.gif")
+ sh("magick", "-size", "8x8", "xc:red", "xc:blue", "-loop", "0", anim)
+ [ap] = one("png", anim) # first frame, no stray frame files
+ assert mime(ap) == "image/png" and not [n for n in os.listdir(out) if n.startswith(".")]
+ clear = os.path.join(src, "clear.png")
+ sh("magick", "-size", "8x8", "xc:none", clear)
+ [cj] = one("jpeg", clear) # transparency flattened onto white
+ assert subprocess.run(["magick", cj, "-format", "%[fx:int(255*p{0,0}.r)]", "info:"],
+ capture_output=True, text=True).stdout == "255"
+ dotted = os.path.join(src, "v1.2 notes.pdf")
+ sh("magick", "-size", "50x50", "xc:white", dotted)
+ names = [os.path.basename(one("pdf-images", dotted)[0]) for _ in range(2)]
+ assert names == ["v1.2 notes (pages)", "v1.2 notes (pages) (1)"], names
+ gz = os.path.join(src, "note.txt.gz")
+ sh("sh", "-c", f"gzip -c {os.path.join(src, 'note.txt')} > '{gz}'")
+ [gx] = one("extract", gz) # a single compressed file
+ assert os.path.basename(gx) == "note.txt" and open(gx).read() == "hello\n"
+ pgz = os.path.join(src, "pic.png.gz")
+ sh("sh", "-c", f"gzip -c '{png}' > '{pgz}'")
+ [px] = one("extract", pgz) # binary content survives
+ assert open(px, "rb").read() == open(png, "rb").read()
+ with open(os.path.join(src, ".bashrc"), "w") as fh:
+ fh.write("x\n")
+ [dz] = one("zip", os.path.join(src, ".bashrc"))
+ assert os.path.basename(dz) == "bashrc.zip"
+ with tempfile.TemporaryDirectory() as tree: # the output folder inside the input
+ inner = os.path.join(tree, "Assistant")
+ with open(os.path.join(tree, "a.txt"), "w") as fh:
+ fh.write("a\n")
+ os.mkfifo(os.path.join(tree, "pipe"))
+ os.symlink("/nonexistent", os.path.join(tree, "dangling"))
+ r, rc = fileops("zip", inner, tree)
+ assert r["ok"], r
+ import zipfile
+ assert sorted(zipfile.ZipFile(r["out"][0]).namelist()) == [os.path.basename(tree) + "/a.txt"]
+ stale = os.path.join(out, ".fileops-999999-0.png")
+ open(stale, "w").close()
+ one("png", png) # a dead run's leftovers are swept
+ assert not os.path.exists(stale)
+
# SIGTERM mid-encode (the canvas's Stop): no ffmpeg left, no partial file.
long_clip = os.path.join(src, "long.mp4")
sh("ffmpeg", "-v", "error", "-f", "lavfi", "-i", "testsrc=duration=20:size=1280x720:rate=30",