Last active
August 20, 2026 19:54
-
-
Save oz123/da9bdb04ce47825be8206b4238e80e0c to your computer and use it in GitHub Desktop.
A script to ease testing and reviewing for https://github.com/mate-desktop/mate-panel/pull/1565
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # | |
| # Build the clock applet with libical/vdir support (no EDS, no Wayland), | |
| # spin up a local Radicale CalDAV server with a few events in the current | |
| # week, sync it down with vdirsyncer, and drive the live MATE panel with | |
| # xdotool to confirm the events show up in the clock applet's calendar | |
| # popup. | |
| # | |
| # Run from the mate-panel source tree root: | |
| # ./test-vdir-clock.sh | |
| # | |
| # Requires: an already-running MATE session (DISPLAY set), sudo access | |
| # for `make install`, python3, xdotool, curl. | |
| set -euo pipefail | |
| REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| WORKDIR="${WORKDIR:-/tmp/clock-vdir-test}" | |
| VENV_DIR="$WORKDIR/venv" | |
| RADICALE_DATA="$WORKDIR/radicale-data" | |
| RADICALE_PORT="${RADICALE_PORT:-5232}" | |
| RADICALE_USER="testuser" | |
| RADICALE_COLLECTION="work" | |
| VDIR_LOCAL_DIR="$WORKDIR/vdir-storage" | |
| VDIRSYNCER_CONFIG="$WORKDIR/vdirsyncer-config" | |
| VDIRSYNCER_STATUS="$WORKDIR/vdirsyncer-status" | |
| log() { printf '\n==> %s\n' "$1"; } | |
| # --------------------------------------------------------------------------- | |
| # 1. Build & install the applet | |
| # --------------------------------------------------------------------------- | |
| build_applet() { | |
| log "Configuring (libical enabled, EDS + Wayland disabled)" | |
| cd "$REPO_ROOT" | |
| autoreconf -fi | |
| ./configure --enable-libical --disable-eds --prefix=/usr --disable-wayland | |
| log "Building" | |
| make -j"$(nproc)" | |
| log "Installing (sudo required)" | |
| sudo make install | |
| } | |
| # --------------------------------------------------------------------------- | |
| # 2. Python venv with radicale + vdirsyncer | |
| # --------------------------------------------------------------------------- | |
| setup_python_env() { | |
| log "Creating venv at $VENV_DIR" | |
| mkdir -p "$WORKDIR" | |
| python3 -m venv "$VENV_DIR" | |
| # shellcheck disable=SC1091 | |
| source "$VENV_DIR/bin/activate" | |
| pip install --upgrade pip | |
| pip install radicale vdirsyncer | |
| } | |
| # --------------------------------------------------------------------------- | |
| # 3. Radicale server with a pre-seeded collection | |
| # --------------------------------------------------------------------------- | |
| seed_events() { | |
| local coll_dir="$1" | |
| mkdir -p "$coll_dir" | |
| # Radicale's filesystem storage is vdir-compatible: one .ics per event | |
| # plus a .Radicale.props JSON file for collection metadata. | |
| cat > "$coll_dir/.Radicale.props" <<EOF | |
| {"tag": "VCALENDAR", "D:displayname": "Work", "ICAL:calendar-color": "#3584e4"} | |
| EOF | |
| local today | |
| today=$(date +%Y%m%d) | |
| # All 3 events land on today so they show up in the calendar popup's | |
| # default "Appointments" view (selected day = today) without needing | |
| # to click around the month grid. | |
| for hour in 09 13 17; do | |
| local uid | |
| uid="test-event-${hour}@example.com" | |
| cat > "$coll_dir/$uid.ics" <<EOF | |
| BEGIN:VCALENDAR | |
| VERSION:2.0 | |
| PRODID:-//test//test//EN | |
| BEGIN:VEVENT | |
| UID:$uid | |
| DTSTAMP:${today}T${hour}0000Z | |
| DTSTART:${today}T${hour}0000Z | |
| DTEND:${today}T$(( 10#$hour + 1 ))0000Z | |
| SUMMARY:Test Event ${hour}:00 (radicale) | |
| DESCRIPTION:Injected by test-vdir-clock.sh | |
| END:VEVENT | |
| END:VCALENDAR | |
| EOF | |
| done | |
| } | |
| start_radicale() { | |
| log "Seeding Radicale collection with events in the current week" | |
| seed_events "$RADICALE_DATA/collection-root/$RADICALE_USER/$RADICALE_COLLECTION" | |
| cat > "$WORKDIR/radicale.conf" <<EOF | |
| [server] | |
| hosts = 127.0.0.1:$RADICALE_PORT | |
| [auth] | |
| type = none | |
| [rights] | |
| type = authenticated | |
| [storage] | |
| filesystem_folder = $RADICALE_DATA | |
| EOF | |
| log "Starting Radicale on 127.0.0.1:$RADICALE_PORT" | |
| radicale --config "$WORKDIR/radicale.conf" & | |
| RADICALE_PID=$! | |
| sleep 2 | |
| curl -sf "http://127.0.0.1:$RADICALE_PORT/$RADICALE_USER/$RADICALE_COLLECTION/" \ | |
| -H "Depth: 1" -X PROPFIND >/dev/null \ | |
| || { echo "Radicale did not come up"; exit 1; } | |
| } | |
| stop_radicale() { | |
| if [[ -n "${RADICALE_PID:-}" ]]; then | |
| log "Stopping Radicale (pid $RADICALE_PID)" | |
| kill "$RADICALE_PID" 2>/dev/null || true | |
| fi | |
| } | |
| trap stop_radicale EXIT | |
| # --------------------------------------------------------------------------- | |
| # 4. vdirsyncer: pull Radicale down into a local vdir | |
| # --------------------------------------------------------------------------- | |
| setup_vdirsyncer() { | |
| log "Writing vdirsyncer config" | |
| mkdir -p "$VDIR_LOCAL_DIR" "$VDIRSYNCER_STATUS" | |
| cat > "$VDIRSYNCER_CONFIG" <<EOF | |
| [general] | |
| status_path = "$VDIRSYNCER_STATUS" | |
| [pair test_pair] | |
| a = "local" | |
| b = "remote" | |
| collections = ["from b"] | |
| conflict_resolution = "b wins" | |
| [storage local] | |
| type = "filesystem" | |
| path = "$VDIR_LOCAL_DIR" | |
| fileext = ".ics" | |
| [storage remote] | |
| type = "caldav" | |
| url = "http://127.0.0.1:$RADICALE_PORT/$RADICALE_USER/" | |
| EOF | |
| export VDIRSYNCER_CONFIG | |
| log "vdirsyncer discover + sync" | |
| vdirsyncer discover test_pair <<< "y" | |
| vdirsyncer sync | |
| log "Pointing clock applet at $VDIR_LOCAL_DIR via GSettings" | |
| # org.mate.panel.applet.clock is relocatable; each panel applet instance | |
| # gets its own prefs path under /org/mate/panel/objects/<id>/prefs/. | |
| local clock_id | |
| clock_id=$(dconf list /org/mate/panel/objects/ | grep -E '^(clock|object-[0-9]+)/$' | while read -r obj; do | |
| iid=$(dconf read "/org/mate/panel/objects/${obj}applet-iid" 2>/dev/null) | |
| [[ "$iid" == *"ClockApplet"* ]] && echo "${obj%/}" && break | |
| done) | |
| if [[ -z "$clock_id" ]]; then | |
| echo "Could not find a clock applet instance in dconf; set vdir-calendar-paths manually:" | |
| echo " gsettings set org.mate.panel.applet.clock:/org/mate/panel/objects/<id>/prefs/ vdir-calendar-paths \"['$VDIR_LOCAL_DIR/$RADICALE_COLLECTION']\"" | |
| else | |
| gsettings set "org.mate.panel.applet.clock:/org/mate/panel/objects/$clock_id/prefs/" \ | |
| vdir-calendar-paths "['$VDIR_LOCAL_DIR/$RADICALE_COLLECTION']" | |
| fi | |
| } | |
| # --------------------------------------------------------------------------- | |
| # 5. Restart the panel and drive it with xdotool | |
| # --------------------------------------------------------------------------- | |
| exercise_clock_applet() { | |
| log "Restarting mate-panel to pick up the freshly installed applet" | |
| mate-panel --replace & | |
| sleep 3 | |
| log "Locating the top panel window and clock applet" | |
| local panel_win best_y="" | |
| for win in $(xdotool search --class "mate-panel"); do | |
| local geo geo_y geo_w | |
| geo=$(xdotool getwindowgeometry --shell "$win") | |
| geo_y=$(grep '^Y=' <<< "$geo" | cut -d= -f2) | |
| geo_w=$(grep '^WIDTH=' <<< "$geo" | cut -d= -f2) | |
| [[ -z "$geo_y" || -z "$geo_w" ]] && continue | |
| # Skip tiny helper/proxy windows mate-panel also owns (e.g. 10x10). | |
| (( geo_w < 200 )) && continue | |
| if [[ -z "$best_y" || "$geo_y" -lt "$best_y" ]]; then | |
| best_y="$geo_y" | |
| panel_win="$win" | |
| fi | |
| done | |
| if [[ -z "${panel_win:-}" ]]; then | |
| echo "Could not find mate-panel window via xdotool" | |
| exit 1 | |
| fi | |
| eval "$(xdotool getwindowgeometry --shell "$panel_win")" | |
| # Default assumption: top panel, clock lives near the right edge. | |
| # Adjust CLOCK_X/CLOCK_Y below if your panel layout differs. | |
| local click_x=$(( X + WIDTH - 60 )) | |
| local click_y=$(( Y + HEIGHT / 2 )) | |
| log "Screenshot before click: $WORKDIR/before-click.png" | |
| import -window "$panel_win" "$WORKDIR/before-click.png" 2>/dev/null || \ | |
| xwd -root -out "$WORKDIR/before-click.xwd" | |
| log "Clicking clock applet at ($click_x, $click_y)" | |
| xdotool mousemove "$click_x" "$click_y" click 1 | |
| sleep 1 | |
| local cal_win | |
| cal_win=$(xdotool search --name "^Calendar$" | head -1 || true) | |
| if [[ -z "$cal_win" ]]; then | |
| echo "Calendar popup did not appear at the guessed coordinates." | |
| echo "Inspect $WORKDIR/before-click.png, adjust click_x/click_y, and retry." | |
| exit 1 | |
| fi | |
| #log "Calendar popup found (window $cal_win), taking screenshot" | |
| #import -window "$cal_win" "$WORKDIR/calendar-popup.png" 2>/dev/null || true | |
| #echo "Verify events visually in $WORKDIR/calendar-popup.png" | |
| } | |
| # --------------------------------------------------------------------------- | |
| main() { | |
| build_applet | |
| setup_python_env | |
| start_radicale | |
| setup_vdirsyncer | |
| exercise_clock_applet | |
| log "Done. Radicale left running for further inspection; Ctrl-C to stop." | |
| wait "$RADICALE_PID" | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment