mirror of
https://git.pumpin.online/funkpower/menologion.git
synced 2026-10-22 18:57:14 +00:00
the documented way to use this did not work
`import qs.horologion.modules` was the README's headline instruction and it
fails outright: Quickshell resolves `qs` against the host config root, not
the subtree, so in an empty shell the type is unavailable and in a populated
one the widget would have silently bound to the host's Config and Theme
instead of its own. The imports inside are now relative and each directory
carries a qmldir, so `import "horologion/modules"` works in any host and the
three pieces really are usable on their own.
- refresh() dropped a request made while a run was in flight, so a
jurisdiction or date set a moment after startup never took effect until
midnight. It queues now.
- The waybar script exited non-zero on its error paths, and waybar hides a
module whose script does that -- so a missing typikon made the module
vanish rather than say so, and the .error CSS was dead. An unrecognised
--format field raised instead of rendering empty, and the tooltip
interpolated unescaped text into Pango markup, which matters because
--update-menaion fetches commemorations from a page nobody here controls.
- The installer aborted the whole run at the point where it printed
'leaving it alone', and a wholly successful run exited 1 because the last
statement was a failed [ -d ] test under set -e. --help demanded typikon,
which is what --help would have told you to install.
- eww painted every day with the feast colour and printed a dangling
separator, because it compares against "" and eww renders a JSON null as
the string "null". The parenthesised ?: form two lines away was already
right. The week list also repeated today.
pip install typikon installed nothing; there is no such package, and that was
the first line of every install path. Pascha now carries its label instead of
being a bare ISO date in a corner.
tests/smoke.sh covers the scripts, which is where all of this lived and where
nothing was covered. It caught two of these while being written.
This commit is contained in:
@@ -15,7 +15,7 @@ changes at midnight and at no other moment.
|
|||||||
`typikon` first, since all three front ends shell out to it:
|
`typikon` first, since all three front ends shell out to it:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ pip install typikon
|
$ pipx install git+https://git.pumpin.online/funkpower/typikon
|
||||||
$ typikon --set-default rocor # or antiochian, oca, greece, ...
|
$ typikon --set-default rocor # or antiochian, oca, greece, ...
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -40,14 +40,19 @@ $ quickshell -p ~/.config/quickshell/horologion/shell.qml
|
|||||||
It sits on the background layer, so it behaves like part of the wallpaper —
|
It sits on the background layer, so it behaves like part of the wallpaper —
|
||||||
always under your windows, never stealing a click.
|
always under your windows, never stealing a click.
|
||||||
|
|
||||||
To drop it into a shell you already have:
|
To drop it into a shell you already have, link this directory in beside your
|
||||||
|
own and import it by path:
|
||||||
|
|
||||||
```qml
|
```qml
|
||||||
import qs.horologion.modules
|
import "horologion/modules"
|
||||||
|
|
||||||
Horologion {}
|
Horologion {}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The imports inside are relative, so the widget always resolves its own
|
||||||
|
`Config` and `Theme` rather than silently binding to yours and inheriting
|
||||||
|
whatever they happen to mean.
|
||||||
|
|
||||||
Three pieces, each usable on its own:
|
Three pieces, each usable on its own:
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
@@ -59,11 +64,16 @@ Three pieces, each usable on its own:
|
|||||||
Set a jurisdiction per-widget if you want one different from your default:
|
Set a jurisdiction per-widget if you want one different from your default:
|
||||||
|
|
||||||
```qml
|
```qml
|
||||||
|
import "horologion/services" // for Typikon
|
||||||
|
|
||||||
Horologion {
|
Horologion {
|
||||||
Component.onCompleted: Typikon.jurisdiction = "moscow"
|
Component.onCompleted: Typikon.jurisdiction = "moscow"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`Typikon.forDate` takes an ISO date and shows a different day, which is how
|
||||||
|
you look at what Great Week will ask of you before it arrives.
|
||||||
|
|
||||||
### Colours
|
### Colours
|
||||||
|
|
||||||
`Theme.qml` reads `~/.config/horologion/colors.json`, or whatever
|
`Theme.qml` reads `~/.config/horologion/colors.json`, or whatever
|
||||||
|
|||||||
+23
-8
@@ -20,12 +20,17 @@ horologion-install [quickshell|waybar|eww|bin|all]
|
|||||||
USAGE
|
USAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Set when something was skipped, so the run can report it in its exit status
|
||||||
|
# without `set -e` tearing the rest of the install down.
|
||||||
|
skipped=0
|
||||||
|
|
||||||
link() {
|
link() {
|
||||||
local from="$1" to="$2"
|
local from="$1" to="$2"
|
||||||
mkdir -p "$(dirname "$to")"
|
mkdir -p "$(dirname "$to")"
|
||||||
if [ -e "$to" ] && [ ! -L "$to" ]; then
|
if [ -e "$to" ] && [ ! -L "$to" ]; then
|
||||||
echo " ! $to exists and is not a link — leaving it alone" >&2
|
echo " ! $to exists and is not a link — leaving it alone" >&2
|
||||||
return 1
|
skipped=1
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
ln -sfn "$from" "$to"
|
ln -sfn "$from" "$to"
|
||||||
echo " $to -> $from"
|
echo " $to -> $from"
|
||||||
@@ -74,13 +79,19 @@ do_eww() {
|
|||||||
NOTE
|
NOTE
|
||||||
}
|
}
|
||||||
|
|
||||||
command -v typikon >/dev/null 2>&1 || {
|
require_typikon() {
|
||||||
|
command -v typikon >/dev/null 2>&1 && return 0
|
||||||
echo "horologion: typikon is not on PATH." >&2
|
echo "horologion: typikon is not on PATH." >&2
|
||||||
echo " pip install typikon" >&2
|
echo " pipx install git+https://git.pumpin.online/funkpower/typikon" >&2
|
||||||
echo " https://git.pumpin.online/funkpower/typikon" >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --help must not be gated behind the dependency it tells you about.
|
||||||
|
case "${1:-all}" in
|
||||||
|
-h|--help|help) usage; exit 0 ;;
|
||||||
|
esac
|
||||||
|
require_typikon
|
||||||
|
|
||||||
case "${1:-all}" in
|
case "${1:-all}" in
|
||||||
quickshell) do_bin; do_quickshell ;;
|
quickshell) do_bin; do_quickshell ;;
|
||||||
waybar) do_bin; do_waybar ;;
|
waybar) do_bin; do_waybar ;;
|
||||||
@@ -88,10 +99,14 @@ case "${1:-all}" in
|
|||||||
bin) do_bin ;;
|
bin) do_bin ;;
|
||||||
all)
|
all)
|
||||||
do_bin
|
do_bin
|
||||||
[ -d "$config/quickshell" ] && do_quickshell
|
# `[ -d x ] && f` as the last statement of the script would make a
|
||||||
[ -d "$config/waybar" ] && do_waybar
|
# perfectly good run exit 1 under `set -e` whenever the directory is
|
||||||
[ -d "$config/eww" ] && do_eww
|
# absent. Hence the `|| true`, and the explicit exit below.
|
||||||
|
[ -d "$config/quickshell" ] && do_quickshell || true
|
||||||
|
[ -d "$config/waybar" ] && do_waybar || true
|
||||||
|
[ -d "$config/eww" ] && do_eww || true
|
||||||
;;
|
;;
|
||||||
-h|--help|help) usage ;;
|
|
||||||
*) usage; exit 2 ;;
|
*) usage; exit 2 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
exit "$skipped"
|
||||||
|
|||||||
+24
-6
@@ -14,6 +14,8 @@ particular law actually binds.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import collections
|
||||||
|
import html
|
||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -41,26 +43,42 @@ def main():
|
|||||||
try:
|
try:
|
||||||
days = run(args.jurisdiction, args.days)
|
days = run(args.jurisdiction, args.days)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
# Exit 0 on purpose, here and below: waybar hides a custom module whose
|
||||||
|
# script exits non-zero, so a non-zero exit would make the module
|
||||||
|
# vanish instead of showing why it could not answer.
|
||||||
print(json.dumps({"text": "typikon?", "tooltip":
|
print(json.dumps({"text": "typikon?", "tooltip":
|
||||||
"typikon is not on PATH", "class": "error"}))
|
"typikon is not on PATH", "class": "error"}))
|
||||||
return 1
|
return 0
|
||||||
except (subprocess.CalledProcessError, ValueError) as exc:
|
except (subprocess.CalledProcessError, ValueError) as exc:
|
||||||
print(json.dumps({"text": "⚠", "tooltip": str(exc), "class": "error"}))
|
print(json.dumps({"text": "⚠", "tooltip": str(exc), "class": "error"}))
|
||||||
return 1
|
return 0
|
||||||
|
|
||||||
today = days[0]
|
today = days[0]
|
||||||
fields = dict(today)
|
# `or ""` would blank a legitimate 0, so only None and missing become "".
|
||||||
|
fields = {k: ("" if v is None else v) for k, v in today.items()}
|
||||||
if args.no_emoji:
|
if args.no_emoji:
|
||||||
fields["emoji"] = ""
|
fields["emoji"] = ""
|
||||||
text = args.format.format(**{k: fields.get(k) or "" for k in fields}).strip()
|
try:
|
||||||
|
# format_map over a defaultdict: a field name that does not exist comes
|
||||||
|
# out empty rather than raising, because the --help text advertises
|
||||||
|
# eight names and someone will always try a ninth.
|
||||||
|
text = args.format.format_map(collections.defaultdict(str, fields)).strip()
|
||||||
|
except (IndexError, ValueError) as exc:
|
||||||
|
print(json.dumps({"text": "\u26a0", "class": "error",
|
||||||
|
"tooltip": f"bad --format: {exc}"}))
|
||||||
|
return 0
|
||||||
|
|
||||||
tip = []
|
tip = []
|
||||||
head = today["title"]
|
head = today["title"]
|
||||||
if today.get("churchDate") and today["churchDate"] not in head:
|
if today.get("churchDate") and today["churchDate"] not in head:
|
||||||
head += f" ({today['churchDate']})"
|
head += f" ({today['churchDate']})"
|
||||||
tip.append(f"<b>{head}</b>")
|
# waybar renders the tooltip as Pango markup, so anything interpolated into
|
||||||
|
# it has to be escaped. Nothing in the shipped data contains an ampersand,
|
||||||
|
# but `typikon --update-menaion` fetches commemorations from a third-party
|
||||||
|
# page, and that is exactly where one arrives.
|
||||||
|
tip.append(f"<b>{html.escape(head)}</b>")
|
||||||
if today.get("cycle"):
|
if today.get("cycle"):
|
||||||
tip.append(f"<i>{today['cycle']}</i>")
|
tip.append(f"<i>{html.escape(today['cycle'])}</i>")
|
||||||
tip.append("")
|
tip.append("")
|
||||||
line = today["fastLabel"]
|
line = today["fastLabel"]
|
||||||
if today.get("obligation") == "obligatory":
|
if today.get("obligation") == "obligatory":
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
+9
-6
@@ -5,8 +5,9 @@
|
|||||||
;; (include "horologion/horologion.yuck")
|
;; (include "horologion/horologion.yuck")
|
||||||
;;
|
;;
|
||||||
;; and put (horologion) in a window, or (horologion-bar) in a bar. Set a
|
;; and put (horologion) in a window, or (horologion-bar) in a bar. Set a
|
||||||
;; jurisdiction by editing the two defpoll commands below; leaving them as
|
;; jurisdiction by adding `--jurisdiction <name>` to both defpoll commands
|
||||||
;; they are follows whatever `typikon --set-default` wrote.
|
;; below; leaving them as they are follows whatever `typikon --set-default`
|
||||||
|
;; wrote.
|
||||||
|
|
||||||
(defpoll typikon-day
|
(defpoll typikon-day
|
||||||
:interval "30m"
|
:interval "30m"
|
||||||
@@ -16,7 +17,9 @@
|
|||||||
(defpoll typikon-week
|
(defpoll typikon-week
|
||||||
:interval "30m"
|
:interval "30m"
|
||||||
:initial "{\"days\":[]}"
|
:initial "{\"days\":[]}"
|
||||||
`typikon --json --days 7`)
|
;; From tomorrow, not today: the head block above already shows today in
|
||||||
|
;; full, and simplexpr has no way to drop the first element of a list.
|
||||||
|
`typikon --json --days 6 --date $(date -d tomorrow +%F)`)
|
||||||
|
|
||||||
;; ── the one-line version, for a bar
|
;; ── the one-line version, for a bar
|
||||||
(defwidget horologion-bar []
|
(defwidget horologion-bar []
|
||||||
@@ -38,7 +41,7 @@
|
|||||||
(box :class "head" :space-evenly false :spacing 11
|
(box :class "head" :space-evenly false :spacing 11
|
||||||
(label :class "glyph-big" :text {typikon-day.emoji ?: ""})
|
(label :class "glyph-big" :text {typikon-day.emoji ?: ""})
|
||||||
(box :orientation "v" :space-evenly false :halign "start" :hexpand true
|
(box :orientation "v" :space-evenly false :halign "start" :hexpand true
|
||||||
(label :class {typikon-day.feast != "" ? "title feast" : "title"}
|
(label :class {(typikon-day.feast ?: "") != "" ? "title feast" : "title"}
|
||||||
:halign "start"
|
:halign "start"
|
||||||
:wrap true
|
:wrap true
|
||||||
:lines 2
|
:lines 2
|
||||||
@@ -47,7 +50,7 @@
|
|||||||
? "sub obligatory" : "sub"}
|
? "sub obligatory" : "sub"}
|
||||||
:halign "start"
|
:halign "start"
|
||||||
:text "${typikon-day.weekday ?: ""}${
|
:text "${typikon-day.weekday ?: ""}${
|
||||||
typikon-day.season != "" ? " · " : ""}${
|
(typikon-day.season ?: "") != "" ? " · " : ""}${
|
||||||
typikon-day.season ?: ""}${
|
typikon-day.season ?: ""}${
|
||||||
typikon-day.obligation == "obligatory"
|
typikon-day.obligation == "obligatory"
|
||||||
? " · obligatory" : ""}")
|
? " · obligatory" : ""}")
|
||||||
@@ -70,7 +73,7 @@
|
|||||||
(box :space-evenly false :spacing 9
|
(box :space-evenly false :spacing 9
|
||||||
(label :class "glyph" :text {d.emoji})
|
(label :class "glyph" :text {d.emoji})
|
||||||
(label :class "weekday" :width 34 :xalign 0 :text {d.weekday})
|
(label :class "weekday" :width 34 :xalign 0 :text {d.weekday})
|
||||||
(label :class {d.feast != "" ? "day feast" : "day"}
|
(label :class {(d.feast ?: "") != "" ? "day feast" : "day"}
|
||||||
:hexpand true :xalign 0 :limit-width 40 :text {d.title}))))
|
:hexpand true :xalign 0 :limit-width 40 :text {d.title}))))
|
||||||
|
|
||||||
(box :class "rule")
|
(box :class "rule")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs
|
import ".."
|
||||||
import qs.services
|
import "../services"
|
||||||
|
|
||||||
Panel {
|
Panel {
|
||||||
id: root
|
id: root
|
||||||
@@ -198,7 +198,8 @@ Panel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: Typikon.pascha
|
// Labelled, because a bare ISO date in a corner is a riddle.
|
||||||
|
text: Typikon.pascha !== "" ? "Pascha " + Typikon.pascha : ""
|
||||||
color: Theme.fgDim
|
color: Theme.fgDim
|
||||||
font.family: Config.font
|
font.family: Config.font
|
||||||
font.pixelSize: Config.fontSize - 4
|
font.pixelSize: Config.fontSize - 4
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs
|
import ".."
|
||||||
import qs.services
|
import "../services"
|
||||||
|
|
||||||
// Chrome for the widget. Slightly translucent so a wallpaper reads through it,
|
// Chrome for the widget. Slightly translucent so a wallpaper reads through it,
|
||||||
// opaque enough to stay legible over a busy picture.
|
// opaque enough to stay legible over a busy picture.
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module horologion.modules
|
||||||
|
Panel 1.0 Panel.qml
|
||||||
|
Horologion 1.0 Horologion.qml
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
module horologion
|
||||||
|
singleton Config 1.0 Config.qml
|
||||||
@@ -46,7 +46,15 @@ Singleton {
|
|||||||
|
|
||||||
readonly property bool obligatory: obligation === "obligatory"
|
readonly property bool obligatory: obligation === "obligatory"
|
||||||
|
|
||||||
function refresh() { if (!proc.running) proc.running = true; }
|
// A request made while a run is in flight is queued, not dropped. Dropping
|
||||||
|
// it meant a jurisdiction or date set a moment after startup never took
|
||||||
|
// effect at all -- the answer stayed yesterday's until midnight.
|
||||||
|
property bool _queued: false
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
if (proc.running) { root._queued = true; return; }
|
||||||
|
proc.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
SystemClock { id: clock; precision: SystemClock.Minutes }
|
SystemClock { id: clock; precision: SystemClock.Minutes }
|
||||||
|
|
||||||
@@ -60,6 +68,7 @@ Singleton {
|
|||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: proc
|
id: proc
|
||||||
|
onExited: if (root._queued) { root._queued = false; root.refresh(); }
|
||||||
command: {
|
command: {
|
||||||
const c = ["typikon", "--json", "--days", String(root.days)];
|
const c = ["typikon", "--json", "--days", String(root.days)];
|
||||||
if (root.jurisdiction !== "")
|
if (root.jurisdiction !== "")
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module horologion.services
|
||||||
|
singleton Typikon 1.0 Typikon.qml
|
||||||
|
singleton Theme 1.0 Theme.qml
|
||||||
@@ -4,9 +4,9 @@ import QtQuick
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import qs
|
import "."
|
||||||
import qs.modules
|
import "modules"
|
||||||
import qs.services
|
import "services"
|
||||||
|
|
||||||
// A runnable widget on its own — `quickshell -p quickshell/shell.qml`.
|
// A runnable widget on its own — `quickshell -p quickshell/shell.qml`.
|
||||||
//
|
//
|
||||||
|
|||||||
+3
-3
@@ -12,9 +12,9 @@ import QtQuick
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import qs
|
import "."
|
||||||
import qs.modules
|
import "modules"
|
||||||
import qs.services
|
import "services"
|
||||||
|
|
||||||
ShellRoot {
|
ShellRoot {
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
|
|||||||
Executable
+84
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Smoke tests for the front ends. Run: tests/smoke.sh
|
||||||
|
#
|
||||||
|
# These exist because every bug found in the first review of this repo was in
|
||||||
|
# the shell and Python around the QML, not in the QML: a --format field that
|
||||||
|
# raised, an installer that aborted where it said it was continuing, an error
|
||||||
|
# path that exited non-zero so waybar hid the message it was trying to show.
|
||||||
|
# None of it was covered by anything.
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
waybar="$root/bin/horologion-waybar"
|
||||||
|
install="$root/bin/horologion-install"
|
||||||
|
pass=0 fail=0
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$tmp"' EXIT
|
||||||
|
|
||||||
|
ok() { pass=$((pass+1)); printf ' ok %s\n' "$1"; }
|
||||||
|
bad() { fail=$((fail+1)); printf ' FAIL %s\n %s\n' "$1" "${2:-}"; }
|
||||||
|
|
||||||
|
check() { # check <name> <expected-exit> <cmd...>
|
||||||
|
local name="$1" want="$2"; shift 2
|
||||||
|
local out; out="$("$@" 2>&1)"; local got=$?
|
||||||
|
[ "$got" = "$want" ] || { bad "$name" "exit $got, wanted $want"; return; }
|
||||||
|
ok "$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
json() { # json <name> <cmd...> -- stdout must parse
|
||||||
|
local name="$1"; shift
|
||||||
|
local out; out="$("$@" 2>/dev/null)"
|
||||||
|
if printf '%s' "$out" | python3 -c 'import json,sys; json.load(sys.stdin)' 2>/dev/null
|
||||||
|
then ok "$name"; else bad "$name" "not JSON: ${out:0:80}"; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo " waybar module"
|
||||||
|
command -v typikon >/dev/null 2>&1 || { echo " SKIP: typikon not on PATH"; exit 77; }
|
||||||
|
json "plain run emits JSON" "$waybar"
|
||||||
|
json "an unknown --format field" "$waybar" --format '{nosuchfield}'
|
||||||
|
json "a positional --format field" "$waybar" --format '{0}'
|
||||||
|
json "an unclosed --format brace" "$waybar" --format '{'
|
||||||
|
json "--no-emoji" "$waybar" --no-emoji
|
||||||
|
json "--days 1" "$waybar" --days 1
|
||||||
|
json "an unknown jurisdiction" "$waybar" -j atlantis
|
||||||
|
# waybar hides a module whose script exits non-zero, so every path must be 0
|
||||||
|
# or the error JSON is never read.
|
||||||
|
# A PATH that still has an interpreter but no typikon -- typikon installs to
|
||||||
|
# ~/.local/bin, which this leaves out. Emptying PATH entirely would only prove
|
||||||
|
# that the shebang cannot find python3.
|
||||||
|
check "exit 0 when typikon is missing" 0 env PATH=/usr/bin:/bin "$waybar"
|
||||||
|
json "JSON when typikon is missing" env PATH=/usr/bin:/bin "$waybar"
|
||||||
|
check "exit 0 on a bad jurisdiction" 0 "$waybar" -j atlantis
|
||||||
|
|
||||||
|
out="$("$waybar" --format '{daysToPascha}' 2>/dev/null)"
|
||||||
|
case "$out" in
|
||||||
|
*'"text": ""'*) bad "a zero field is not blanked" "daysToPascha rendered empty" ;;
|
||||||
|
*) ok "a zero field is not blanked" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo " installer"
|
||||||
|
check "--help without typikon" 0 env PATH=/usr/bin:/bin "$install" --help
|
||||||
|
check "an unknown subcommand" 2 "$install" nonsense
|
||||||
|
mkdir -p "$tmp/a/.config/quickshell" "$tmp/a/.config/waybar"
|
||||||
|
check "a clean run with eww absent" 0 env HOME="$tmp/a" XDG_CONFIG_HOME="$tmp/a/.config" "$install" all
|
||||||
|
mkdir -p "$tmp/b/.local/bin" "$tmp/b/.config"; : > "$tmp/b/.local/bin/horologion-waybar"
|
||||||
|
check "a real file at the target" 1 env HOME="$tmp/b" XDG_CONFIG_HOME="$tmp/b/.config" "$install" all
|
||||||
|
[ -L "$tmp/a/.local/bin/horologion-waybar" ] \
|
||||||
|
&& ok "the clean run actually linked" || bad "the clean run actually linked"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo " syntax"
|
||||||
|
check "installer parses" 0 bash -n "$install"
|
||||||
|
check "waybar module compiles" 0 python3 -m py_compile "$waybar"
|
||||||
|
rm -rf "$root/bin/__pycache__"
|
||||||
|
for f in "$root"/quickshell/*.qml "$root"/quickshell/*/*.qml; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
grep -q . "$f" || bad "empty QML: $f"
|
||||||
|
done
|
||||||
|
ok "QML files are non-empty"
|
||||||
|
|
||||||
|
echo
|
||||||
|
printf ' %d passed, %d failed\n\n' "$pass" "$fail"
|
||||||
|
[ "$fail" -eq 0 ]
|
||||||
Reference in New Issue
Block a user