Fix pale Wi-Fi/Bluetooth tray icons, das-panel-icon help, publisher and deployer (0.9.5)
- Light theme uses Papirus-Light: the panel icons of Papirus are #dfdfdf, made for dark panels (xsettings, theme index, login screen). - das-panel-icon help/-h/--help print usage instead of setting an icon; unknown options are rejected. - New packages publisher (publish) and deployer (deploy) on the desktop image: config in ~/.config/<name>/ (neutral example copied on first run), history in ~/.local/state/<name>/. - das-theme 0.3.0, das-settings 0.6.0. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from publisher import config, detect, packages, pipeline
|
||||
from publisher.packages import BINARY, KINDS, Published
|
||||
|
||||
APK, APPIMAGE, TARGZ = KINDS[0], KINDS[1], KINDS[-1]
|
||||
|
||||
SERVER = """
|
||||
[server]
|
||||
host = "h"
|
||||
apps_dir = "/srv/www/apps"
|
||||
"""
|
||||
|
||||
|
||||
def write(tmp_path: Path, body: str) -> Path:
|
||||
p = tmp_path / "publish.toml"
|
||||
p.write_text(SERVER + body)
|
||||
return p
|
||||
|
||||
|
||||
def touch(path: Path, data: bytes = b"x", exe: bool = False) -> Path:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(data)
|
||||
if exe:
|
||||
path.chmod(0o755)
|
||||
return path
|
||||
|
||||
|
||||
def test_load(tmp_path):
|
||||
cfg = config.load(write(tmp_path, """
|
||||
[[app]]
|
||||
name = "Foo"
|
||||
src = "~/x"
|
||||
packages = ["app/release/*.apk"]
|
||||
keep = 1
|
||||
"""))
|
||||
a = cfg.app("foo")
|
||||
assert a.src_path == Path.home() / "x"
|
||||
assert cfg.keep(a) == 1
|
||||
assert a.local("/abs/f") == Path("/abs/f")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("body, msg", [
|
||||
('name = "Foo_v1"\nsrc = "x"\npackages = ["a"]', "bad name"),
|
||||
('name = "Foo 2"\nsrc = "x"\npackages = ["a"]', "bad name"),
|
||||
('name = "Foo"\nsrc = "x"', "packages"),
|
||||
('name = "Foo"\nsrc = "x"\npackages = ["../a"]', "glob"),
|
||||
('name = "Foo"\nsrc = "x"\npackages = ["a"]\nicon = "i.jpg"', "icon"),
|
||||
('name = "Foo"\nsrc = "x"\npackages = ["a"]\nversion_regex = "v.*"', "group"),
|
||||
('name = "Foo"\nsrc = "x"\npackages = ["a"]\ntypo = 1', "unknown key"),
|
||||
('name = "Foo"\nsrc = "x"\npackages = "a"', "wrong type"),
|
||||
])
|
||||
def test_validation(tmp_path, body, msg):
|
||||
with pytest.raises(config.ConfigError, match=msg):
|
||||
config.load(write(tmp_path, "[[app]]\n" + body))
|
||||
|
||||
|
||||
def test_unsafe_apps_dir(tmp_path):
|
||||
p = tmp_path / "publish.toml"
|
||||
p.write_text('[server]\napps_dir = "/srv"\n')
|
||||
with pytest.raises(config.ConfigError, match="apps_dir"):
|
||||
config.load(p)
|
||||
|
||||
|
||||
def test_append_round_trip(tmp_path):
|
||||
p = write(tmp_path, "# keep me\n")
|
||||
app = config.App(name="n", src="~/p", packages=["a/*.apk"], build_env={"JAVA_HOME": "~/jdk"}, build='echo "hi"')
|
||||
config.append_apps(p, [app])
|
||||
assert "# keep me" in p.read_text()
|
||||
loaded = config.load(p).app("n")
|
||||
assert loaded.build == 'echo "hi"' and loaded.build_env == {"JAVA_HOME": "~/jdk"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name, version", [
|
||||
("Foo_v1.2.0.apk", "1.2.0"), ("Foo_v1.2.0-release.apk", "1.2.0"), ("Foo-1.5-release.apk", "1.5"),
|
||||
("ExtMess_v0.7.0_linux_x86_64.AppImage", "0.7.0"), ("ExtMess_v0.7.0_linux_amd64.deb", "0.7.0"),
|
||||
("Stylometer_v0.9.0_linux.tar.gz", "0.9.0"),
|
||||
("app-release.apk", ""), ("multitext", ""),
|
||||
])
|
||||
def test_file_version(name, version):
|
||||
assert packages.file_version(Path(name)) == version
|
||||
|
||||
|
||||
@pytest.mark.parametrize("file, exe, app, version, kind", [
|
||||
("DasWeather_v1.4.0.apk", False, "DasWeather", "1.4.0", APK),
|
||||
("WhereAreYou_v1.1.0.AppImage", False, "WhereAreYou", "1.1.0", APPIMAGE),
|
||||
("multitext_v0.6", True, "multitext", "0.6", BINARY),
|
||||
("Foo_v1.0-release.apk", False, "Foo", "1.0-release", APK),
|
||||
("Stilometer_Linux_v0.9.0.tar.gz", False, "Stilometer_Linux", "0.9.0", TARGZ),
|
||||
])
|
||||
def test_parse_published(file, exe, app, version, kind):
|
||||
p = packages.parse_published(file, exe)
|
||||
assert (p.app, p.version, p.kind) == (app, version, kind)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("file, exe", [("README.txt", True), ("Foo.png", False), ("multitext_v0.6", False),
|
||||
(".Foo_v1.apk.Xy12", False), ("run.sh", True)])
|
||||
def test_not_packages(file, exe):
|
||||
assert packages.parse_published(file, exe) is None
|
||||
|
||||
|
||||
def test_version_key():
|
||||
assert packages.version_key("1.10.0") > packages.version_key("1.9.3")
|
||||
assert packages.version_key("1.0") == packages.version_key("1.0.0")
|
||||
assert packages.version_key("1.2.0-release") == packages.version_key("1.2.0")
|
||||
|
||||
|
||||
def test_resolve(tmp_path):
|
||||
touch(tmp_path / "app/release/Foo_v1.9.0.apk")
|
||||
touch(tmp_path / "app/release/Foo_v1.10.0-release.apk")
|
||||
touch(tmp_path / "build/foo", exe=True)
|
||||
touch(tmp_path / "app/build.gradle.kts", b'android {\n versionName = "1.11.0"\n}\n')
|
||||
app = config.App(name="Foo", src=str(tmp_path), packages=["app/release/*.apk", "build/foo", "dist/*.deb"],
|
||||
version_file="app/build.gradle.kts")
|
||||
builds, notes = packages.resolve(app)
|
||||
assert [(b.site_name("Foo"), b.version) for b in builds] == [("Foo_v1.10.0.apk", "1.10.0"),
|
||||
("Foo_v1.11.0", "1.11.0")]
|
||||
assert any("rebuild?" in n for n in notes) and any("dist/*.deb" in n for n in notes)
|
||||
|
||||
|
||||
def test_resolve_best_of_several_places(tmp_path):
|
||||
touch(tmp_path / "app/release/Foo_v1.0.apk", b"old")
|
||||
touch(tmp_path / "app/build/outputs/apk/release/Foo_v1.1.apk", b"new")
|
||||
app = config.App(name="Foo", src=str(tmp_path),
|
||||
packages=["app/release/*.apk", "app/build/outputs/apk/release/*.apk"])
|
||||
builds, notes = packages.resolve(app)
|
||||
assert [b.path.name for b in builds] == ["Foo_v1.1.apk"] and notes == []
|
||||
|
||||
|
||||
def test_resolve_same_version_newest_wins(tmp_path):
|
||||
a = touch(tmp_path / "app/release/Foo_v1.0.apk", b"studio")
|
||||
b = touch(tmp_path / "app/build/outputs/apk/release/Foo_v1.0.apk", b"gradle!")
|
||||
os.utime(a, (1000, 1000))
|
||||
app = config.App(name="Foo", src=str(tmp_path), packages=["app/release/*.apk", "app/build/outputs/apk/release/*.apk"])
|
||||
builds, notes = packages.resolve(app)
|
||||
assert builds[0].path == b
|
||||
assert any("2 different builds" in n and "app/build/outputs" in n for n in notes)
|
||||
|
||||
|
||||
def test_resolve_needs_a_version(tmp_path):
|
||||
touch(tmp_path / "build/foo", exe=True)
|
||||
with pytest.raises(packages.PackageError, match="no version"):
|
||||
packages.resolve(config.App(name="Foo", src=str(tmp_path), packages=["build/foo"]))
|
||||
|
||||
|
||||
def test_prune():
|
||||
pub = [Published(f"F_v{v}.apk", "F", v, APK) for v in ("1.0", "1.10", "1.9", "1.2")]
|
||||
pub.append(Published("F_v1.0.AppImage", "F", "1.0", APPIMAGE))
|
||||
assert sorted(p.file for p in pipeline.prune(pub, 2)) == ["F_v1.0.apk", "F_v1.2.apk"]
|
||||
assert pipeline.prune(pub, 0) == []
|
||||
|
||||
|
||||
def fake_state(monkeypatch, files: dict[str, tuple[int, bool]], hashes: dict[str, str]):
|
||||
def state(cfg, hash_names=()):
|
||||
st = pipeline.RemoteState({n: (s, 1.0, ex) for n, (s, ex) in files.items()})
|
||||
st.hashes = {n: h for n, h in hashes.items() if n in hash_names}
|
||||
return st
|
||||
monkeypatch.setattr(pipeline, "remote_state", state)
|
||||
monkeypatch.setattr(pipeline, "git_rev", lambda src: "")
|
||||
|
||||
|
||||
def test_preview(tmp_path, monkeypatch):
|
||||
apk = touch(tmp_path / "app/release/Foo_v1.3.0.apk", b"new")
|
||||
img = touch(tmp_path / "dist/Foo_v1.3.0_linux_x86_64.AppImage", b"img")
|
||||
info = touch(tmp_path / "site.txt", b"Foo\nabout")
|
||||
fake_state(monkeypatch, {
|
||||
"Foo_v1.0.0.apk": (1, False), "Foo_v1.1.0.apk": (1, False), "Foo_v1.2.0.apk": (1, False),
|
||||
"Foo_v1.0.0.txt": (1, False), "Foo_v1.3.0.AppImage": (1, True), "Foo.txt": (1, False),
|
||||
"Other_v9.apk": (1, False),
|
||||
}, {"Foo_v1.3.0.AppImage": pipeline.sha256(img), "Foo.txt": pipeline.sha256(info)})
|
||||
cfg = config.load(write(tmp_path, f'[[app]]\nname = "Foo"\nsrc = "{tmp_path}"\n'
|
||||
'packages = ["app/release/*.apk", "dist/*.AppImage"]\ninfo = "site.txt"\n'))
|
||||
plan = pipeline.preview(cfg, cfg.app("Foo"), pipeline.Options(notes="fixes"), log=lambda s: None)
|
||||
got = {(i.action, i.target) for i in plan.items}
|
||||
assert got == {("new", "Foo_v1.3.0.apk"), ("same", "Foo_v1.3.0.AppImage"), ("notes", "Foo_v1.3.0.txt"),
|
||||
("delete", "Foo_v1.0.0.apk"), ("delete", "Foo_v1.0.0.txt")}
|
||||
assert next(i for i in plan.items if i.target == "Foo_v1.3.0.apk").local == apk
|
||||
|
||||
|
||||
def test_preview_older_and_changed(tmp_path, monkeypatch):
|
||||
touch(tmp_path / "Foo_v1.0.apk", b"old")
|
||||
touch(tmp_path / "Foo_v2.0.deb", b"changed")
|
||||
fake_state(monkeypatch, {"Foo_v1.1.apk": (1, False), "Foo_v2.0.deb": (1, False)}, {"Foo_v2.0.deb": "0" * 64})
|
||||
cfg = config.load(write(tmp_path, f'[[app]]\nname = "Foo"\nsrc = "{tmp_path}"\npackages = ["*.apk", "*.deb"]\n'))
|
||||
plan = pipeline.preview(cfg, cfg.app("Foo"), pipeline.Options(), log=lambda s: None)
|
||||
assert [i.action for i in plan.items] == ["skip", "skip"] and not plan.changes
|
||||
plan = pipeline.preview(cfg, cfg.app("Foo"), pipeline.Options(force=True, prune=False), log=lambda s: None)
|
||||
assert [i.action for i in plan.items] == ["new", "replace"]
|
||||
|
||||
|
||||
def test_detect_android(tmp_path):
|
||||
touch(tmp_path / "settings.gradle.kts", b'rootProject.name = "DasPressureAndMedicines"\n')
|
||||
touch(tmp_path / "app/build.gradle.kts", b'versionName = "3.13.0"\n')
|
||||
touch(tmp_path / "app/release/DasPressureMedicines_v3.13.0.apk")
|
||||
touch(tmp_path / "desktop/dist/DasPressureMedicines_v3.13.0_linux_x86_64.AppImage", exe=True)
|
||||
app = detect.new_app(tmp_path)
|
||||
assert app.name == "DasPressureMedicines"
|
||||
assert app.packages == ["app/release/*.apk", "app/build/outputs/apk/release/*.apk", "desktop/dist/*.AppImage"]
|
||||
assert app.version_file == "app/build.gradle.kts"
|
||||
config.validate_app(app)
|
||||
|
||||
|
||||
def test_detect_go(tmp_path):
|
||||
touch(tmp_path / "internal/version/version.go", b'package version\n\nconst (\n\tVersion = "0.10.0"\n)\n')
|
||||
touch(tmp_path / "build/multitext", exe=True)
|
||||
app = detect.new_app(tmp_path, "multitext")
|
||||
assert (app.packages, app.version_file) == (["build/multitext"], "internal/version/version.go")
|
||||
builds, _ = packages.resolve(app)
|
||||
assert builds[0].site_name("multitext") == "multitext_v0.10.0"
|
||||
|
||||
|
||||
def test_site_name_cleanup():
|
||||
assert detect._site_name("Das Pressure 2") == "DasPressure2"
|
||||
assert os.path.basename(detect._home(Path.home() / "a")) == "a"
|
||||
Reference in New Issue
Block a user